En plus des méthodes de classe ordinaires, il y a un certain
nombre de méthodes spéciales que les classes
Python peuvent définir. Au lieu d'être
appelées directement par votre code (comme les méthodes ordinaires)
les méthodes spéciales sont appelées pour vous par
Python dans des circonstances particulières
ou quand une syntaxe spécifique est utilisée.
Comme vous l'avez vu dans la section précédente, les méthodes
ordinaires nous ont permis en grande partie d'envelopper un dictionnaire
dans une classe. Mais les méthodes ordinaires seules ne suffisent pas
parce qu'il y a beaucoup de choses que vous pouvez faire avec un
dictionnaire en dehors d'appeler ses méthodes. Pour commencer vous
pouvez lire (get) et
écrire (set) des éléments
à l'aide d'une syntaxe qui ne fait pas explicitement appel à des
méthodes. C'est là que les méthodes de classe spéciales interviennent :
elle fournissent un moyen de faire correspondre la syntaxe n'appelant
pas de méthodes à des appels de méthodes.
5.6.1. Lire et écrire des éléments
Exemple 5.12. La méthode spéciale __getitem__
def __getitem__(self, key): return self.data[key]
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3")>>> f{'name':'/music/_singles/kairo.mp3'}>>> f.__getitem__("name")'/music/_singles/kairo.mp3'>>> f["name"]'/music/_singles/kairo.mp3'
La méthode spéciale __getitem__ à l'air
simple. Comme les méthodes ordinaires clear,
keys et values, elle ne
fait que rediriger vers le dictionnaire pour obtenir sa valeur.
Mais comment est-elle appelée ? Vous pouvez appeler
__getitem__ directement, mais en pratique
vous ne le ferez pas, je le fais ici seulement pour vous montrer
comment ça marche. La bonne manière d'utiliser
__getitem__ est d'obtenir de
Python qu'il fasse l'appel pour
vous.
Cela à l'apparence exacte de la syntaxe que l'on utilise
pour obtenir une valeur
d'un dictionnaire et cela retourne bien la valeur que l'on
attend. Mais il y a un chaînon manquant : en coulisse,
Python convertit cette syntaxe en un
appel de méthode f.__getitem__("name"). C'est
pourquoi __getitem__ est une méthode de
classe spéciale, non seulement vous pouvez l'appeler vous-même,
mais vous pouvez faire en sorte que
Python l'appelle pour vous grâce à la
syntaxe appropriée.
Bien sûr, Python a une méthode spéciale __setitem__ pour accompagner __getitem__, comme nous le montrons dans l'exemple suivant.
Comme la méthode __getitem__,
__setitem__ redirige simplement l'appel au
véritable dictionnaire self.data. Et comme pour
__getitem__, vous n'appelez pas
directement cette méthode en général,
Python appelle
__setitem__ pour vous lorsque vous utilisez
la bonne syntaxe.
Cela ressemble à la syntaxe habituelle d'utilisation d'un
dictionnaire, mais en fait f est une classe
faisant de son mieux pour passer pour un dictionnaire et
__setitem__ est un élément essentiel de cette
apparence. Cette ligne de code appelle en fait
f.__setitem__("genre", 32) en coulisse.
__setitem__ est une méthode de classe
spéciale car elle est appelée pour vous, mais c'est quand même une
méthode de classe. Nous pouvons la redéfinir dans une classe descendante
tout aussi facilement qu'elle a été définie dans
UserDict. Cela nous permet de définir des classes
qui se comportent en partie comme des dictionnaires, mais qui ont leur
propre comportement dépassant le cadre d'un simple dictionnaire.
Ce concept est à la base de tout le
framework que nous étudions dans ce
chapitre. Chaque type de fichier peut avoir une classe de manipulation
qui sait comment obtenir des méta-données d'un type particulier de
fichier. Une fois certains attributs (comme le nom et l'emplacement du
fichier) connus, la classe de manipulation sait comment obtenir les
autres attributs automatiquement. Cela se fait en redéfinissant la
méthode __setitem__, en cherchant des clés
particulières et en ajoutant un traitement supplémentaire quand elles
sont trouvées.
Par exemple, MP3FileInfo est un descendant
de FileInfo. Quand le nom
(name) d'un MP3FileInfo est
défini, cela ne change pas seulement la valeur de la clé
name (comme pour l'ancêtre
FileInfo), mais déclenche la recherche de balises
MP3 et définit tout un ensemble de clés.
Exemple 5.14. Redéfinition de __setitem__ dans MP3FileInfo
Notez que notre méthode __setitem__ est
définie exactement comme la méthode héritée. C'est important car
Python appellera la méthode pour nous
et qu'il attend un certain nombre d'arguments (techniquement
parlant, les noms des arguments n'ont pas d'importance, seulement
leur nombre).
Voici le point crucial de toute la classe
MP3FileInfo : si nous assignons une valeur
à la clé name, alors nous voulons faire quelque
chose en plus.
Le traitement supplémentaire que nous faisons pour les noms
(name) est encapsulé dans la méthode
__parse. C'est une autre méthode de classe
définie dans MP3FileInfo et quand nous
l'appelons nous la qualifions avec self. Un
appel à __parse tout court chercherait une
fonction ordinaire définie hors de la classe, ce qui n'est pas ce
que nous voulons, appeler self.__parse
cherchera une méthode définie dans la classe. Cela n'a rien de
nouveau, c'est de la même manière que l'on fait référence aux
données
attributs.
Après avoir fait notre traitement supplémentaire, nous
voulons appeler la méthode héritée. Rappelez-vous que
Python ne le fait jamais pour vous,
vous devez le faire manuellement. Notez que nous appelons
l'ancêtre immédiat, FileInfo, même si il
n'a pas de méthode __setitem__. Cela
fonctionne parce que Python va remonter
la hierarchie d'heritage jusqu'à ce qu'il trouve une classe avec
la méthode que nous appelons, cette ligne finira donc par trouver
et appeler la méthode __setitem__ définie
dans UserDict.
Lorsque vous accédez à des données attributs dans une classe,
vous devez qualifier le nom de l'attribut :
self.attribute. Lorsque
vous appelez d'autres méthodes dans une classe, vous devez qualifier
le nom de la méthode :
self.method.
D'abord nous créons une instance de
MP3FileInfo sans lui passer de nom de
fichier (nous pouvons le faire parce que l'argument
filename de la méthode
__init__ est optionnel). Comme
MP3FileInfo n'a pas de méthode
__init__ propre, Python
remonte la hierarchie d'héritage et trouve la méthode
__init__ de FileInfo.
Cette méthode __init__ appelle manuellement
la méthode __init__ de
UserDict puis définit la clé
name à la valeur de
filename, qui est None
puisque nous n'avons passé aucun nom de fichier. Donc
mp3file est au début un dictionnaire avec une
clé, name, dont la valeur est
None.
Maintenant les choses sérieuses commencent. Définir la clé
name de mp3file déclenche la
méthode __setitem__ de
MP3FileInfo (pas
UserDict), qui remarque que nous
définissons la clé name avec une valeur réelle
et appelle self.__parse. Bien que nous
n'ayons pas encore vu le contenu de la méthode
__parse, vous pouvez voir à partir de la
sortie qu'elle définit plusieurs autres clés :
album, artist,
genre, title,
year et comment.
Modifier la clé name recommencera le même
processus : Python appelle
__setitem__, qui appelle
self.__parse, qui définit toutes les autres
clés.