• Aucun résultat trouvé

Méthodes spéciales

Dans le document Python Python (Page 156-163)

Il est possible en Python de définir d’autres méthodes spéciales que __init__() et

__del__(), qui déterminent un fonctionnement spécifique pour une classe lorsqu’elle est utilisée dans certaines opérations.

Ces méthodes permettent de faire varier le comportement des objets et sont regrou-pées en fonction des cas d’utilisation :

• représentation et comparaison de l’objet ;

• utilisation de l’objet comme fonction ;

• accès aux attributs de l’objet ;

• utilisation de l’objet comme conteneur ;

• utilisation de l’objet comme type numérique.

Représentation et comparaison de l’objet

__str__()

Appelée par la primitive str(). Doit renvoyer une représentation sous forme de chaîne de caractères d’un objet. Cette représentation peut être un transtypage de l’objet en objet string lorsque c’est possible ou une représentation plus informelle.

Str()

__repr__()

Appelée par la primitive repr(). Similaire à __str__() sauf que la représentation doit être une expression Python telle que eval(repr(a)) == a lorsque c’est possible.

>>> class Car:

... _defaults = ['bruyante']

... qualities = ['rapide', 'economique']

...

>> class A:

... def __str__(self):

... return 'je suis un objet de type A' ...

>>> a = A()

>>> str(a)

'je suis un objet de type A'

__repr__() doit donc permettre de recréer l’objet. Si le reverse n’est pas possible,

__repr__() doit renvoyer une string de la forme '<description>'. Les instances de classe renvoient en général leur adresse mémoire.

__cmp__(other)

Utilisée par tous les opérateurs de comparaison lorsque l’objet est impliqué.

__cmp__() doit renvoyer :

• un entier négatif si self est inférieur à other ;

• un entier positif si self est supérieur à other;

• zéro en cas d’égalité.

[__lt__, __le__, __eq__, __ne__, __gt__, __ge__](other)

Ensemble de méthodes de comparaison, qui sont utilisées de préférence à __cmp__() si elles sont présentes, pour chacun des opérateurs. Ces méthodes doivent renvoyer

True ou False ;

a < b correspond à a.__lt__(b);

a <= b correspond à a.__le__(b);

a == b correspond à a.__eq__(b);

a != b correspond à a.__ne__(b);

a > b correspond à a.__gt__(b);

a >= b correspond à a.__ge__(b).

Il n’y a aucun contrôle d’intégrité sur ces opérateurs : __ne__ et __eq__ peuvent tous les deux renvoyer True. Lorsqu’une méthode est implémentée, il est donc conseillé de toujours implémenter la méthode symétrique pour assurer l’intégrité.

Enfin, si ni __cmp__(),ni __eq__ et __ne__ ne sont définies, la primitive id() sera utilisée pour la comparaison.

__hash__()

Appelée par la primitive hash() ou par un objet dictionnaire lorsque l’objet est utilisé comme clé. Doit renvoyer un entier de 32 bits. Si deux objets sont définis comme égaux, par __cmp__(), __eq__() ou __ne__(), __hash__() doit renvoyer la même valeur pour ces deux objets.

__nonzero__()

Appelée par la primitive bool() et par la comparaison avec True ou False. Doit ren-voyer True ou False. Lorsque cette méthode n’est pas définie, c’est __len__() qui est utilisée. __len__() représente la taille de l’objet. Si aucune des deux méthodes n’est présente, l’objet est toujours considéré comme vrai.

__unicode__()

Appelée par la primitive unicode(). Doit renvoyer un objet de type unicode. Si la méthode n’est pas implémentée, une conversion en string est tentée, puis un pas-sage de string à unicode.

Utilisation de l’objet comme fonction

Lorsqu’une instance d’objet est appelée comme une fonction, c’est __call__() qui est appelée si elle est définie. Les objets de cette classe deviennent, au même titre qu’une fonction ou une méthode, des objets callable.

Class callable

Accès aux attributs de l’objet

Lorsque l’interpréteur rencontre une écriture de type objet.attribut, il utilise le dictionnaire interne __dict__ pour rechercher cet attribut, et remonte dans les dic-tionnaires des classes dérivées si nécessaire.

L’utilisation des trois méthodes suivantes permet d’influer sur ce fonctionnement.

__setattr__()

__setattr__() est utilisée lorsqu’une valeur est assignée, en lieu et place d’une modi-fication classique de l’attribut __dict__ de l’objet.

objet.attribut = 'valeur' devient équivalent à objet.__setattr__('attribut', 'valeur')

Le code contenu dans __setattr__() ne doit pas appeler directement l’attribut à mettre à jour, au risque de s’appeler lui-même récursivement. Il faut utiliser un accès à __dict__.

__getattr__() et __getattribute__()

__getattr__() est appelée en dernier recours lorsqu’un attribut est recherché dans un objet. Cette méthode ne surcharge pas le fonctionnement normal afin de permettre à

__setattr__(), lorsqu’elle est surchargée, d’accéder aux attributs normalement.

>>> class A:

... def __call__(self, one, two):

... return one + two ...

>>> a = A()

>>> callable(a) True

>>> a(1, 6) 7

Les new-style class, présentées dans la prochaine section, introduisent cependant une nouvelle méthode __getattribute__(), qui comme __setattr__() permet de sur-charger complètement l’accès aux attributs.

__delattr__()

Complément des deux méthodes précédentes, objet.__delattr__('attribut') est équivalent à del objet.attribut.

Essais sur les attributs de mapping

Utilisation de l’objet comme conteneur

Les mappings et les séquences sont tous des objets de type conteneurs, qui implé-mentent un tronc commun de méthodes. Ces méthodes sont présentées ci-dessous et peuvent être définies dans toute classe.

__getitem__(key)

Utilisée lorsqu’une évaluation de type objet[key] est effectuée. Pour les objets de type séquences, key doit être un entier positif ou un objet de type slice. Les map-pings, quant à eux, utilisent des clés de tout type non modifiable.

Si la clé fournie n’est pas d’un type compatible, une erreur TypeError est retournée.

Enfin, si la clé est en dehors des valeurs autorisées, une erreur de type IndexError est retournée.

__setitem__(key, value)

Utilisée lorsqu’une assignation de type objet[key] = valeur est effectuée. Les mêmes erreurs peuvent être utilisées que celles de __getitem__. Les mappings ajou-tent automatiquement la clé lorsqu’elle n’existe pas, contrairement aux séquences qui retournent une erreur si la clé n’existe pas.

__delitem__(key)

Permet de supprimer une entrée du conteneur.

__len__()

Appelée par la primitive len(), et permet de renvoyer le nombre d’éléments du con-teneur.

__iter__()

Appelée par la primitive iter(), et doit renvoyer un iterator capable de parcourir les éléments.

__contains__(item)

Renvoie vrai si item se trouve parmi les éléments.

Un peu de contenu

>>> class MyContainer:

... def __init__(self):

... self._data = {}

... def __getitem__(self, key):

... if key in self._data:

... return self._data[key]

... else:

... print("Je n'ai pas %s" % key) ... def __setitem__(self, key, value):

... self._data[key] = value ... def __delitem__(self, key):

... print('on ne fait pas ca chez moi')

Utilisation de l’objet comme type numérique

Ces méthodes peuvent être utilisées pour définir le fonctionnement de l’objet lorsqu’il est employé dans toute opération numérique, que ce soit une addition, un décalage de bits vers la gauche, ou encore une inversion. Chacune de ces méthodes renvoie en général l’objet lui-même, qui est l’opérande de gauche, pour assurer une logique au niveau des opérateurs, mais peut dans certains cas renvoyer l’opérande de droite ou un tout autre objet.

... def __len__(self):

... return len(self._data) ... def __contains__(self, item):

... return item in self._data.values() ...

>>> inside = MyContainer()

>>> inside['12']

Je n'ai pas 12

>>> inside['la_cle'] = 45

>>> inside['la_cle']

45

>>> len(inside) 1

>>> del inside['la_cle']

on ne fait pas ca chez moi

>>> inside['la_cle2'] = 34

>>> len(inside) 2

Tableau 5–1 Méthodes pour les opérateurs numériques

Méthode Opération Variations

__add__(other) objet + other R et I

__sub__(other) Objet - other R et I

__mul__(other) objet * other R et I

__floordiv__(other) objet // other R et I

__mod__(other) objet % other R et I

__divmod__(other) divmod(objet, other) R et I

__pow__(other[, modulo]) objet ** other R et I

__lshift__(other) objet << other R et I

__rshift__(other) objet >> other R et I

__and__(other) objet & other R et I

__xor__(other) objet ^ other R et I

__or__(other) objet | other R et I

__div__(other) objet / other R et I

Pour toutes ces méthodes, un appel à objet opérateur other déclenche un appel à

objet.methode(other).

La variation I ajoute un préfixe i à la méthode (__iadd__(), __imul__(), etc.) et permet de définir les opérateurs augmentés +=, *=, etc. Cette variation renvoie en général objet augmenté de other.

La variation R ajoute un préfixe r à la méthode (__radd__(), __rmul__(), etc.) et permet de définir des opérateurs inversés : other.operateur(object) est appelé en lieu et place de objet.operateur(other). Lorsque l’opération classique n’est pas supportée, l’interpréteur tente l’opération inverse.

Surcharge de l’addition

__truediv__(other) objet / other R et I

__neg__() - objet

__pos__() + objet

__abs__() abs(objet)

__invert__() ~ objet

__complex__() complex(objet)

__int__() int(objet)

__long__() long(objet)

__float__() float(objet)

__oct__() oct(objet)

__hex__() hex(objet)

__coerce__(other) coerce(objet, other)

>>> class Additionable:

... def __init__(self, value):

... self.value = value ... def __add__(self, other):

... return Additionable(self.value + other.value ) ... def __iadd__(self, other):

... return self.__add__(other) ... def __str__(self):

... return str(self.value) ...

>>> val1 = Additionable(5)

>>> val2 = Additionable(12)

>>> val3 = val1 + val2

>>> str(val3) '17'

Tableau 5–1 Méthodes pour les opérateurs numériques (suite)

Méthode Opération Variations

Dans le document Python Python (Page 156-163)