Programmation orientée objet

Une classe Date

Définir une classe Date avec trois attributs:

Lorsqu'un attribut concerne tous les objets d'une même classe, on parle d'attribut de classe

Ainsi pour la lisibilité des objets on va introduire un attribut de classe à la classe Date, sous la forme d'une liste nom_mois, permettant l'affichage d'une date sous la forme "15 septembre 2021" plutôt que 15/09/21


class Date:
    nom_mois = ['#', 'janvier','février',...]
    #à compléter
    def __init__(self,j,m,a):
        pass

Cet attribut de classe peut être utilisé sous la forme Date.nom_mois

Avec les méthodes suivantes :

On peut vouloir comparer des dates. Python propose une méthode particulière à définir pour cela __lt__(self,other) qui renvoie Vrai si l'objet en cours de construction self est strictement plus petit que la date other

Compléter le code suivant et tester le


def __lt__(self,other):
    return self.annee < other.annee or\
    (self.annee == other.annee and self.mois < other.mois) or\
    #... à compléter

Il existe aussi des méthodes qui ne s'appliquent pas à un objet en particulier mais servent de fonction d'aide pour tous les objets définis par la classe

On peut soit définir cette fonction à l'extérieur de la classe et l'appeler à l'intérieur de la classe où l'insérer dans la classe sans self dans les paramètres

Compléter le code suivant


def max(date1,date2):
    if date1.annee > date2.annee :
        return date1
    elif date2.annee > date1.annee :
        return date2
    elif date1.mois > date1.mois :
        # à compléter

Si on appelle cette méthode de classe à l'extérieur de la classe on procède ainsi par exemple


date1 = Date(12,09,2021)
date2 = Date(12,03,2022)
print(Date.max(date1,date2))

Gestion des erreurs

A quel moment contrôle-t-on la validité des données?

La classe Date telle que l'on a écrit précedemment suppose que l'on a "filtré" les dates avant de les insérer dans le constructeur

Si on a oublié de le faire on peut se retrouver avec des dates comme 32/15/2021 !

Pire les valeurs ne sont peut être pas entières!

Nous sommes habitués aux messages d'erreur dans la console

S'il y a plusieurs erreurs dans un programme, une erreur de syntaxe ou une erreur de calcul, le programme s'arrête à la première erreur rencontrée

Dans le langage Python un certain nombre d'erreurs est déjà répertorié comme par exemple une division par zéro

Dans notre cas nous devons définir ce qui est invalide et sera considéré comme une erreur qui arrêtera le programme dès son interception

Dans un premier temps on va définir une méthode de classe jour_est_valide(j) où on teste si le jour est de type entier et est compris entre 1 et 31, si ce n'est pas le cas on lève une erreur qui est un objet de type ValueError avec un message approprié "le jour "+ str(j) + " est invalide" passé en paramètre du constructeur de l'objet ValueError


class Date2:
    #attribut de classe
    nom_mois = ["#",'janvier','fevrier','mars','avril','mai',
    'juin','juillet','août','septembre','octobre','novembre','décembre']
    
    #méthodes de classe
    def jour_est_valide(j):
        return isinstance(j,int) and (1 <=  j <= 31)                

Ensuite dans le constructeur on écrit

def __init__(self,j,m,a):
        if Date2.jour_est_valide(j):
            self.jour = j
        else:
            raise ValueError("le jour "+ str(j) + " est invalide")

Compléter le code pour tester le mois et l'année puis ensuite réaliser les tests suivants


date1 = Date2((2,1),1,2022)
date2 = Date2("Lundi",1,2022)
date3 = Date2("Lundi",15,2022)

On observe que le programme s'arrête dès la première erreur car le jour est de type tuple au lieu de int, les deux dernières instructions ne sont pas exécutées

Pour que le programme soit exécuté dans son intégralité malgré les erreurs rencontrées on encadre les sources d'erreurs possibles par le bloc try ...except dans le constructeur


class Date3:
    #attribut de classe
    nom_mois = ["#",'janvier','fevrier','mars','avril','mai',
    'juin','juillet','août','septembre','octobre','novembre','décembre']
    
    #méthode de classe
    def jour_est_valide(j):
        return isinstance(j,int) and (1 <=  j <= 31)
        
    def __init__(self,j,m,a):
        try:
            if Date3.jour_est_valide(j):
                self.jour =  j
            else:
                raise ValueError
            
        except:
            print("le jour "+ str(j) + " est invalide")
    

Compléter le constructeur et réaliser les mêmes tests que précédemment et observer que cette fois ci le programme ne s'arrête pas et délivre tous les messages d'erreur d'un coup

Trier des fichiers

Un système d'exploitation propose de trier les fichiers contenus dans un répertoire par différentes entrées possibles(en simplifiant):

Définir une classe Fichier, les attributs sont nom de type str, date de type Date (voir plus haut) et taille de type int

On commence par définir une méthode __lt__(self,other) qui retourne vrai si un fichier est strictement plus petit qu'un autre suivant la date d'ajout

On rappelle qu'en Première on a implémenté le tri par insertion pour des entiers ou des chaînes de caractères


def insertion(T,i):
    """
    La liste T contient des entiers
    ou des chaines de caractères
    est triée de l'indice 0
    jusqu'à l'indice i - 1 
    i >= 1
    On insère T[i] à sa place et à la fin 
    La liste T est triée de l'indice 0
    jusqu'à l'indice i 
    """    
    
    temp = T[i]
    j = i
    while j > 0 and T[j-1] > temp:
        T[j] = T[j-1]
        j -= 1
    T[j] = temp

def tri_insertion(T):
    """
    tri par insertion la liste T 
    contenant des nombres ou des chaines de caractères
    """
    for i in range(1,len(T)):
        insertion(T,i)

nous allons l'adapter pour des fichiers avec le comparateur défini ci-dessus


def insertion(T,i):
    """
    La liste T contient des fichiers
    comparables suivant leur date d'ajout
    est triée de l'indice 0
    jusqu'à l'indice i - 1 
    i >= 1
    On insère T[i] à sa place et à la fin 
    La liste T est triée de l'indice 0
    jusqu'à l'indice i 
    """    
    
    temp = T[i]
    j = i
    #on peut utiliser > à cause de __lt__()
    while j > 0 and T[j-1] > temp:
        T[j] = T[j-1]
        j -= 1
    T[j] = temp

def tri_insertion(T):
    """
    tri par insertion la liste T 
    contenant des nombres ou des chaines de caractères
    """
    for i in range(1,len(T)):
        insertion(T,i)

On n'a résolu que partiellement le problème car on ne peut pas utiliser la fonction tri_insertion(T) pour trier les fichiers suivant leur taille ou leur nom

Pour plus de généralité on va donc créer un objet Comparateur ayant un attribut nom, valant soit 'taille', soit 'nom' et soit 'date'

Cet objet n'a qu'une seule méthode plus_petit(self,x,y) qui en fonction du nom du comparateur compare les fichiers x et y avec l'opérateur <

Ce qui suppose que pour un attribut d'un autre type que int,float ou str la méthode __lt__() ait été implémentée pour donner un sens à l'opérateur <


class Comparateur:
    
    def __init__(self,nom):
        self.nom = nom
    
    def plus_petit(self,x,y):
        if self.nom== 'taille':
            return x.taille < y.taille
        if self.nom == 'date':
            return x.date < y.date
        if self.nom == 'nom':
            return x.nom < y.nom

Maintenant on peut trier les fichiers suivant n'importe quel attribut du fichier en ajoutant comme paramètre à insertion(T,i) et tri_insertion(T) un comparateur comp


def insertion(T,i,comp):
    """
    La liste T est triée de l'indice 0
    jusqu'à l'indice i - 1 
    i >= 1
    On insère T[i] à sa place et à la fin 
    La liste T est triée de l'indice 0
    jusqu'à l'indice i 
    """    
    
    temp = T[i]
    j = i
    while j > 0 and comp.plus_petit(temp,T[j-1]):
        T[j] = T[j-1]
        j -= 1
    T[j] = temp

def tri_insertion(T,comp):
    """
    tri par insertion la liste T 
    contenant des nombres ou des chaines de caractères
    """
    for i in range(1,len(T)):
        insertion(T,i,comp)

A l'exécution on peut avoir par exemple


date1 = Date(15,1,2022)
date2 = Date(17,1,2022)
date3 = Date(15,2,2022)

fic1 = Fichier('programme1',date1,1000)
fic2 = Fichier('texte1',date2,300)
fic3 = Fichier('image',date3,100)
   
c1 = Comparateur('nom')
c2 = Comparateur('date')
c3 = Comparateur('taille')
    
liste_fichiers = [fic1,fic2,fic3]
#tri suivant le nom   
tri_insertion(liste_fichiers,c1)
#tri suivant la date  
tri_insertion(liste_fichiers,c2)
#tri suivant la taille   
tri_insertion(liste_fichiers,c3)