Membres de classe

Les membres (c’est-à-dire les attributs et les méthodes) ont été définis jusqu’ici au niveau des instances. Mais on peut en définir aussi au niveau d’une classe.
1. Attribut de classe
Contrairement à un attribut d’instance, propre à chaque instance, un attribut de classe est partagé par toutes les instances de la classe.
Exemple : on voudrait que le classe Fraction
ait 2 comportements distincts au choix :
soit les instances se simplifient automatiquement, soit elles restent inchangés.
On veut décider en début de programme si l’on veut que les fractions se simplifient ou pas.
Cette propriété s’applique alors à l’ensemble de l’application, pour chaque instance.
Ce qui veut dire que si la classe est en mode simplification, alors toutes instances du programme vont se simplifier.
Pour cela, on va utiliser un attribut de classe (en l’occurence ici reduction
) pour mémoriser le mode de fonctionnement courant :
from math import gcd
class Fraction:
reduction = True (1)
def __init__(self,n,d):
self._num = n
self._den = d
if Fraction.reduction: (2)
self.reduire()
def switch_reduction(self):
Fraction.reduction = not Fraction.reduction (2)
def reduire(self):
if self._den==0:
raise Exception("le dénominateur est nul")
pgcd = gcd(self._num,self._den)
self._num //= pgcd
self._den //= pgcd
def __str__(self):
return f"{self._num}/{self._den}"
1 | reduction est un attribut de classe |
2 | chaque instance accède à cet attribut de classe |
ce qui s’utilise par exemple comme :
if __name__=="__main__":
q1 = Fraction(21,35)
q2 = Fraction(9,45)
print(q1,q2) # 3/5 1/5
q1.switch_reduction()
q3 = Fraction(21,35)
q4 = Fraction(9,45)
print(q3,q4) # 21/35 9/45
Cet attribut a une existence indépendante de toute instance. On peut donc le référencer même si aucune instance n’existe :
if __name__=="__main__":
print(Fraction.reduction) # True
Cet attribut est atteignable aussi via n’importe quelle instance :
if __name__=="__main__":
q1 = Fraction(1,2)
q2 = Fraction(2,3)
print(Fraction.reduction) # True
print(q1.reduction) # True
print(q2.reduction) # True
q1.switch_reduction()
print(Fraction.reduction) # False
print(q1.reduction) # False
print(q2.reduction) # False
C’est embêtant de référencer la classe
car renommer la classe Fraction nécessiterait de modifier le code de cette méthode.
L’attribut
|
2. Méthode de classe
C’est une méthode définie au niveau de la classe.
Elle a donc accès aux attributs de la classe,
mais pas à ceux des instances (elle ne possède pas le paramètre self
).
Son périmètre d’action est donc restreint à la classe : elle ne peut appeler que des méthodes de classe.
Dans l’exemple précédent, la méthode switch_reduction()
n’a pas pour objectif d’interagir avec les instances de Fraction
, c’est donc typiquement une méthode de classe :
class Fraction:
reduction = True (1)
def __init__(self,n,d):
self._num = n
self._den = d
if Fraction.reduction: (2)
Fraction.reduire()
@classmethod (1)
def switch_reduction(cls):
cls.reduction = not cls.reduction (2)
1 | ceci indique que la méthode qui suit est une méthode de classe |
Conséquence :
le premier paramètre d’une méthode de classe ne reçoit pas une instance,
mais la classe de l’instance pour laquelle elle est appelée
(c’est pourquoi il est nommé par convention cls
) :
Une méthode de classe a une existence indépendante de toute instance. Elle peut donc être appelée sans qu’aucune instance existe :
if __name__=="__main__":
print(Fraction.reduction) # True
Fraction.switch_reduction()
print(Fraction.reduction) # False