[wiki: Méthodes spéciales] [#license ©]
#!comment
A traiter :
nonzero
Constructeur et destructeur
Le constructeur est une méthode spéciale, appelée init(), qui est appelée lors de la création d’une instance de classe.
Le destructeur est une méthode spéciale, appelée del(), qui est appelée lors de la destruction d’une instance. ATTENTION : on ne contrôle pas vraiment le moment ou le destructeur est appelé. De ce fait, il est rarement utilisé.
#!div style="width:600px"
{{{
#!python
class Vecteur:
def init(self,u=0,v=0):
self.__x = u
self.__y = v
def getx(self):
return self.__x
def gety(self):
return self.__y
def norm(self):
x2 = self.__x**2
y2 = self.__y**2
return (x2+ y2)**(1/2)
v1 = Vecteur(3, 4)
print(v1.getx(),v1.gety())
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
3 4
</pre>
#!python
v2 = Vecteur(-2, 7)
print(v2.getx(),v2.gety())
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
-2 7
</pre>
}}}
Affichage
Par défaut, l’affichage d’une instance renvoie son nom de classe et son adresse en mémoire :
#!div style="width:600px"
{{{
#!python
class Vecteur:
def init(self,u=0,v=0):
self.__x = u
self.__y = v
v = Vecteur(3,4)
print(v)
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
<main.Vecteur object at ...>
</pre>
}}}
On peut personnaliser cet affichage en s’appuyant sur les méthodes spéciales str() et repr() :
#!div style="width:600px"
{{{
#!python
class Vecteur:
def init(self,u=0,v=0):
self.__x = u
self.__y = v
def str(self):
return "({:.0f},{:.0f})".format(self.x,self.y)
def repr(self):
return "Vecteur({:.0f},{:.0f})".format(self.x,self.y)
v = Vecteur(3,4)
print(v)
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(3,4)
</pre>
#!python
v
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
Vecteur(3,4)
</pre>
}}}
En cas d’affichage destiné à l’utilisateur, tel qu’un appel à print(), c’est la méthode str() qui est recherchée en priorité, et seulement en dernier recours la méthode repr(). En principe, cette dernière est supposée retourner un texte qui, si il était exécuté, recréerait l’objet original.
Objets-fonctions
En dotant une classe de la méthode call, les instances de cette classe peuvent être utilisées comme des fonctions. On parle d’objets-fonctions. Cela peut-être utile lorsqu’on veut utiliser un programme générique qui attend une fonction en paramètre, et que l’on veut lui donner à la place des objets qui savent se comporter en fonction, tout en ayant des paramètres internes.
#!div style="width:600px"
{{{
#!python
class FonctionLineaire:
def init(self,constant):
self.constant = constant
def call(self,other):
return self.constant*other
fois2 = FonctionLineaire(2)
print(fois2(6))
print(map(fois2,[1,2,3,4,5]))
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
12
[2, 4, 6, 8, 10]</pre>
}}}
Ce mécanisme est souvent le plus sain pour associer des données persistantes à une fonction, plutôt que l’utilisation de variables globales, de fonctions imbriquées, ou de valeurs par défaut modifiables.
Opérateurs mathématiques
Il est possible de permettre l’utilisation des opérateurs habituels avec les nouvelles classes, en redéfinissant des méthodes spéciales au nom prédéfini :
+:add(self,other)-:sub(self,other)*:mul(self,other)/:truediv(self,other)//:floordiv(self,other)%:mod(self,other)**:pow(self,other)
#!div style="width:600px"
{{{
#!python
class Vecteur:
def init(self,u=0,v=0):
self.__x = u
self.__y = v
def add(self, other):
if isinstance(other, Vecteur):
newx = self.x + other.x
newy = self.y + other.y
return Vecteur(newx, newy)
def str(self):
return "({:.0f},{:.0f}".format(self.x,self.y)
v1 = Vecteur(2,3)
v2 = Vecteur(-4,7)
v3 = v1 + v2
print(v3)
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(-2,10)
</pre>
}}}
Index de séquence
En dotant une classe de la méthode getitem(), on peut utiliser ses instances avec toutes les formes de [] :
#!div style="width:600px"
{{{
#!python
class MonTexte:
def init(self,value):
self.data = value
def getitem(self,index):
return self.data[index]
txt = MonTexte("Bonjour")
print(txt[2])
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
n
</pre>
#!python
print(txt[-1::-1])
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
ruojnoB
</pre>
}}}
En bonus, la présence de getitem() permet d’utiliser un objet au sein d’une boucle for. L’interpréteur va invoquer cette méthode avec des valeurs entières croissantes, en commencant par 0, et jusqu’à l’émission d’une exception IndexError.
#!div style="width:600px"
{{{
#!python
for c in txt:
print(c)
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
B
o
n
j
o
u
r
</pre>
}}}
En réalité, la présence de getitem() permet d’utiliser un objet dans de multiples autres « contextes d’itération », tels que qu’un test d’appartenance in, une « compréhension de liste », la fonction prédéfinie map, l’assignation de séquences, …
#!div style="width:600px"
{{{
#!python
'i' in txt
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
False
</pre>
#!python
[c for c in txt]
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
['B', 'o', 'n', 'j', 'o', 'u', 'r']
</pre>
#!python
map(ord,txt)
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
[66, 111, 110, 106, 111, 117, 114]
</pre>
#!python
(c1, c2, c3, c4, c5, c6, c7) = txt
c3, c2, c1
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
('n', 'o', 'B')
</pre>
#!python
list(txt), tuple(txt), '-'.join(txt)
#!html
<pre style="margin-left:22px;margin-right:22px;border: 1px solid lightgoldenrodyellow;padding:10px;background-color:lightyellow">
(['B', 'o', 'n', 'j', 'o', 'u', 'r'],
('B', 'o', 'n', 'j', 'o', 'u', 'r'),
'B-o-n-j-o-u-r')
</pre>
}}}
#!comment
Itérateurs
- Un objet peut être utilisé comme itérateur si sa classe définit :
iter(self)next(self) # En python 3 : next
{{{
#!div style="width:600px"
{{{
#!python
>>> class Reverse:
... def init(self, data):
... self.data = data
... self.index = len(data)
... def iter(self):
... return self
... def next(self):
... if self.index == 0:
... raise StopIteration
... self.index = self.index - 1
... return self.data[self.index]
>>> for char in Reverse('Jour'):
... print(char)
r
u
o
J
}}}
}}}
Travaux Pratiques
1. Dotez la classe Expression d’un constructeur à partir d’une chaîne de caractère, (et y fondre la fonction analyse()).
1. Transformez la méthode display() en méthode str().
1. Dotez la classe Expression d’un comportement de séquence (Transformez paires() en getitem()).
1. Dotez la classe Calculatrice d’un constructeur, puis transformez là en objet-fonction.
le fichier « calculatrice.py » contient à présent :
#!python
.....
expr = Expression(chaine)
print(expr)
for op, num in expr:
if op=='=': res = num
else:
fct = operateurs.operations[op]
res = fct(res,num)
.....
et le fichier « main.py » :
#!python
calc = Calculatrice()
print()
ligne=input(">>> ")
while ligne!='OFF':
calc(ligne)
ligne=input(">>> ")
© David Chamont, Laboratoire Leprince-Ringuet (LLR) / IN2P3 / CNRS, Ecole polytechnique / Université Paris-Saclaybr
© Ce(tte) œuvre est mise à disposition selon les termes de la Licence Creative Commons Attribution – Partage dans les Mêmes Conditions 4.0 International [=#license]