Introduction
Divers aspects du comportement de Python peuvent être personnalisés via des méthodes spéciales ou dites "magiques". Cette section présente cette idée. En outre, l'accès dynamique aux attributs et les méthodes liées sont également discutés.
Introduction
Les classes peuvent définir des méthodes spéciales. Ces méthodes ont un sens particulier pour l'interpréteur Python. Elles sont toujours précédées et suivies de __. Par exemple __init__.
class Stock(object):
def __init__(self):
...
def __repr__(self):
...
Il existe des dizaines de méthodes spéciales, mais nous ne considérerons que quelques exemples spécifiques.
Méthodes spéciales pour les conversions de chaînes de caractères
Les objets ont deux représentations sous forme de chaîne de caractères.
>>> from datetime import date
>>> d = date(2012, 12, 21)
>>> print(d)
2012-12-21
>>> d
datetime.date(2012, 12, 21)
>>>
La fonction str() est utilisée pour créer une sortie imprimable agréable:
>>> str(d)
'2012-12-21'
>>>
La fonction repr() est utilisée pour créer une représentation plus détaillée pour les programmeurs.
>>> repr(d)
'datetime.date(2012, 12, 21)'
>>>
Ces fonctions, str() et repr(), utilisent une paire de méthodes spéciales dans la classe pour produire la chaîne de caractères à afficher.
class Date(object):
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
## Utilisée avec `str()`
def __str__(self):
return f'{self.year}-{self.month}-{self.day}'
## Utilisée avec `repr()`
def __repr__(self):
return f'Date({self.year},{self.month},{self.day})'
Nota: La convention pour __repr__() est de retourner une chaîne de caractères qui, lorsqu'elle est passée à eval(), recréera l'objet sous-jacent. Si cela n'est pas possible, une représentation facilement lisible est utilisée à la place.
Méthodes spéciales pour les mathématiques
Les opérateurs mathématiques impliquent des appels aux méthodes suivantes.
a + b a.__add__(b)
a - b a.__sub__(b)
a * b a.__mul__(b)
a / b a.__truediv__(b)
a // b a.__floordiv__(b)
a % b a.__mod__(b)
a << b a.__lshift__(b)
a >> b a.__rshift__(b)
a & b a.__and__(b)
a | b a.__or__(b)
a ^ b a.__xor__(b)
a ** b a.__pow__(b)
-a a.__neg__()
~a a.__invert__()
abs(a) a.__abs__()
Méthodes spéciales pour l'accès aux éléments
Ce sont les méthodes pour implémenter des conteneurs.
len(x) x.__len__()
x[a] x.__getitem__(a)
x[a] = v x.__setitem__(a,v)
del x[a] x.__delitem__(a)
Vous pouvez les utiliser dans vos classes.
class Sequence:
def __len__(self):
...
def __getitem__(self,a):
...
def __setitem__(self,a,v):
...
def __delitem__(self,a):
...
Appel de méthode
L'appel d'une méthode est un processus en deux étapes.
- Recherche : L'opérateur
. - Appel de méthode : L'opérateur
()
>>> s = stock.Stock('GOOG',100,490.10)
>>> c = s.cost ## Recherche
>>> c
<bound method Stock.cost of <Stock object at 0x590d0>>
>>> c() ## Appel de méthode
49010.0
>>>
Méthodes liées
Une méthode qui n'a pas encore été appelée par l'opérateur d'appel de fonction () est connue sous le nom de méthode liée. Elle opère sur l'instance à partir de laquelle elle est issue.
>>> s = stock.Stock('GOOG', 100, 490.10)
>>> s
<Stock object at 0x590d0>
>>> c = s.cost
>>> c
<bound method Stock.cost of <Stock object at 0x590d0>>
>>> c()
49010.0
>>>
Les méthodes liées sont souvent à l'origine d'erreurs non évidentes dues à la négligence. Par exemple :
>>> s = stock.Stock('GOOG', 100, 490.10)
>>> print('Cost : %0.2f' % s.cost)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: float argument required
>>>
Ou un comportement trompeur difficile à déboguer.
f = open(filename, 'w')
...
f.close ## Oups, rien n'a été fait du tout. `f` est toujours ouvert.
Dans les deux cas, l'erreur est causée par l'oubli d'inclure les parenthèses finales. Par exemple, s.cost() ou f.close().
Accès aux attributs
Il existe une manière alternative d'accéder, de manipuler et de gérer les attributs.
getattr(obj, 'name') ## Identique à obj.name
setattr(obj, 'name', value) ## Identique à obj.name = value
delattr(obj, 'name') ## Identique à del obj.name
hasattr(obj, 'name') ## Vérifie si l'attribut existe
Exemple :
if hasattr(obj, 'x'):
x = getattr(obj, 'x'):
else:
x = None
*Remarque : getattr() a également une valeur par défaut utile pour l'*argument*.
x = getattr(obj, 'x', None)
Exercice 4.9 : Meilleure sortie pour l'affichage d'objets
Modifiez l'objet Stock que vous avez défini dans stock.py de sorte que la méthode __repr__() produise une sortie plus utile. Par exemple :
>>> goog = stock.Stock('GOOG', 100, 490.1)
>>> goog
Stock('GOOG', 100, 490.1)
>>>
Voyez ce qui se passe lorsque vous lisez un portefeuille d'actions et que vous visualisez la liste résultante après avoir effectué ces modifications. Par exemple :
>>> import report
>>> portfolio = report.read_portfolio('portfolio.csv')
>>> portfolio
... voir ce que la sortie est...
>>>
Exercice 4.10 : Un exemple d'utilisation de getattr()
getattr() est un mécanisme alternatif pour lire les attributs. Il peut être utilisé pour écrire du code extrêmement flexible. Pour commencer, essayez cet exemple :
>>> import stock
>>> s = stock.Stock('GOOG', 100, 490.1)
>>> columns = ['name','shares']
>>> for colname in columns:
print(colname, '=', getattr(s, colname))
name = GOOG
shares = 100
>>>
Observez attentivement que les données de sortie sont déterminées entièrement par les noms d'attributs listés dans la variable columns.
Dans le fichier tableformat.py, prenez cette idée et développez-la en une fonction généralisée print_table() qui imprime un tableau montrant les attributs spécifiés par l'utilisateur d'une liste d'objets arbitraires. Comme avec la fonction print_report() précédente, print_table() devrait également accepter une instance TableFormatter pour contrôler le format de sortie. Voici comment cela devrait fonctionner :
>>> import report
>>> portfolio = report.read_portfolio('portfolio.csv')
>>> from tableformat import create_formatter, print_table
>>> formatter = create_formatter('txt')
>>> print_table(portfolio, ['name','shares'], formatter)
name shares
---------- ----------
AA 100
IBM 50
CAT 150
MSFT 200
GE 95
MSFT 50
IBM 100
>>> print_table(portfolio, ['name','shares', 'price'], formatter)
name shares price
---------- ---------- ----------
AA 100 32.2
IBM 50 91.1
CAT 150 83.44
MSFT 200 51.23
GE 95 40.37
MSFT 50 65.1
IBM 100 70.44
>>>
Sommaire
Félicitations ! Vous avez terminé le laboratoire sur les méthodes spéciales. Vous pouvez pratiquer d'autres laboratoires sur LabEx pour améliorer vos compétences.