Programmation objet en python

  • classes
  • méthodes : constructeurs, etc
  • surcharge d'opérateurs et fonctions

Que faut-il pour définir une classe ?

En python, seul le constructeur est obligatoire, et se définit avec le nom réservé __init__

Il n'y a pas besoin de destructeurs explicites, les objets sont libérés en l'absence de référence active pointant sur eux.

In [ ]:
 
In [42]:
class Duree:
    """une classe pour manipuler des durées temporelles, en heures/minutes/secondes
    """
    def __init__(self,nbsec):
        # attribut public
        self.total = nbsec
        # attribut privé
        self.__bof = 0
         
    def heures(self):
        return self.total//3600
    
    def minutes(self):
        return (self.total % 3600)//60
    
    def secondes(self):
        return self.total % 60
    
    def __repr__(self):
        return "%02d:%02d:%02d"%(self.heures(),self.minutes(),self.secondes())
    
    def __add__(self,other):
        return Duree(self.total+other.total)
      
    def __sub__(self,other):
        return Duree(abs(self.total-other.total))
        
a = Duree(4567)
b = Duree(10010)
print("a =",a,"; b =",b)
print("a+b =",a+b)
print("a-b =",a-b)
a = 01:16:07 ; b = 02:46:50
a+b = 04:02:57
a-b = 01:30:43

Autres méthodes courantes

De même que __init__ est une méthode spéciale, on peut redéfinir certaines méthodes suivantes pour avoir des effets particuliers :

  • __repr__(self) définit la chaine qui s'affiche quand on fait un 'print'
  • __getitem__ peut définir des accès avec des indices si besoin est, en lecture (... = x[0])
  • __setitem__ peut définir des accès avec des indices si besoin est, en écriture (x[0]= ...)
  • __contains__ peut redéfinir l'opérateur in

Les opérateurs mathématiques peuvent être surchargés pour prendre comme arguments des objets de la classe considérée, en redéfinissant les méthodes suivantes:

  • __add__(self, other) : pour utiliser +
  • __sub__(self, other) : -
  • __mul__(self, other) : *

Héritage

Le lien d'héritage en python se définit de façon classique, par surcharge des méthodes.

In [17]:
class Rectangle: 
    def __init__(self,largeur,longueur):
        self.large = largeur
        self.long = longueur        
    
    def aire(self):
        return self.large*self.long
    
    def get_largeur(self):
        return self.large
    
    def get_longueur(self):
        return self.long
    
    def __repr__(self):
        return str(self.long)+"x"+str(self.large)
In [24]:
class Carre(Rectangle):
    def __init__(self,cote):
        Rectangle.__init__(self,cote,cote)
        self.blabla = 0
        
    def get_cote(self):
        return self.get_longueur()
    
r = Rectangle(10,30)
c = Carre(10)
print(r)
print(c)
print(c.get_cote())
30x10
10x10
10

Les objets sont définis par référence, comme toutes les données mutables en python.

Comment faire pour avoir des objets similaires distincts ? Il faut faire des copies :

In [36]:
import copy
c = copy.copy(a)
c is a
Out[36]:
False

Mais cette copie est "superficielle", elle ne fait que recopier les attributs. Si l'attribut est lui-même un pointeur sur un objet structuré, il n'est pas distinct.

Il faut alors utiliser une "copie profonde" :

In [35]:
c = copy.deepcopy(a)

Héritage multiple

On peut faire hériter une classe de plusieurs classes pour récupérer tous les traits et attributs en les combinant.

En cas de conflit pour une méthode, la priorité est sur la première classe héritée.

In [40]:
class A:
    def m(self):
        print("appel de A.m")

class B(A):
    def m(self):
        print("appel de B.m")
    
class C(A):
    def m(self):
        print("appel de C.m")

# héritage multiple 
class D(B,C):
    pass

a = D()
a.m() 
appel de B.m