Python est entièrement orienté objet
: vous pouvez définir vos propres classes, hériter de vos classes ou
des classes prédéfinies et instancier les classes que vous avez
défini.
Définir une classe en Python est
simple, comme pour les fonctions, il n'y a pas de définition séparée
d'interface. Vous définissez simplement la classe et commencez à coder.
Une classe Python commence par le mot réservé
class suivi du nom de la classe. Techiquement c'est
tout ce qui est requis, une classe n'hérite pas obligatoirement d'une
autre.
Exemple 5.3. La classe Python la plus simple
class Loaf: pass
Le nom de cette classe est Loaf et elle n'hérite d'aucune autre classe. Chaque mot d'un nom de classe prend habituellement une majuscule, DeCetteManiere, mais c'est une simple convention et pas une nécéssité.
Cette classe ne définit aucune méthode ni attribut, mais
pour respecter la syntaxe, il est nécéssaire d'avoir quelque chose
dans la définition, nous utilisons donc pass.
C'est un mot réservé de Python qui
signifie simplement «circulez, il n'y a rien à voir».
C'est une instruction qui ne fait rien et c'est un bon marqueur
lorsque vous écrivez un squelette de fonction ou de classe.
Vous l'aurez sans doute deviné, tout est indenté dans une
classe, comme le code d'une fonction, d'une instruction
if, d'une boucle for etc.
La première ligne non indenté ne fait plus partie de la
classe.
L'instruction pass de
Python est comme une paire d'accolades vide
({}) en Java ou
C.
Bien sûr, dans des cas réels la plupart des classes hériteront
d'autres classes et elles définiront leurs propres classes, méthodes et
attributs. Mais comme vous l'avez vu, il n'y a rien qu'une classe doit
absolument avoir en dehors d'un nom. En particulier, les programmeurs
C++ s'étonneront sans
doute que les classes Python n'aient pas de
constructeurs et de destructeurs explicites. Les classes
Python ont quelque chose de semblable à un
constructeur : la méthode __init__.
Exemple 5.4. Définition de la classe FileInfo
from UserDict import UserDict
class FileInfo(UserDict):
En Python, l'ancêtre d'une classe
est simplement indiqué entre parenthèses immédiatement après le
nom de la classe. La classe FileInfo est
hérite donc de la classe UserDict (qui
a été importée du module
UserDict).
UserDict est une classe qui se comporte
comme un dictionnaire, vous permettant pratiquement de dériver le
type de données dictionnaire et d'y ajouter votre propre
comportement (il y a des classes semblables
UserList et
UserString qui vous permettent de dériver
les listes et les chaînes). Il y a un peu de magie noire derrière
tout cela, nous la démystifierons plus loin dans ce chapitre
lorsque nous explorerons la classe UserDict
plus en détail.
En Python, l'ancêtre d'une classe est
simplement indiqué entre parenthèses immédiatement après le nom de la
classe. Il n'y a pas de mot clé spécifique comme
extends en Java.
Python supporte l'héritage multiple. Entre
les parenthèses qui suivent le nom de classe, vous pouvez indiquer
autant de classes ancêtres que vous le souhaitez, séparées par des
virgules.
5.3.1. Initialisation et écriture de classes
Cet exemple montre l'initialisation de la classe FileInfo avec la méthode __init__.
Exemple 5.5. Initialisation de la classe FileInfo
class FileInfo(UserDict):
"store file metadata"def __init__(self, filename=None):
Les classes peuvent aussi (et le devraient) avoir une doc string, comme les modules et les fonctions.
__init__ est appelé immédiatement après
qu'une instance de la classe est créée. Il serait tentant mais
incorrect de l'appeler le constructeur de la classe. Tentant,
parceque ça ressemble à un constructeur (par convention,
__init__ est la première méthode définie de
la classe), ça se comporte comme un constructeur (c'est le premier
morceau de code exécuté dans une nouvelle instance de la classe)
et que ça sonne pareil («init» fait penser à quelque
chose comme un constructeur). Incorrect, parce qu'au moment ou
__init__ est appelé, l'objet à déjà été créé
et qu vous avez déjà une référence valide à la nouvelle instance
de la classe. Mais __init__ est ce qui se
rapproche le plus d'un constructeur en
Python et remplit en gros le même
rôle.
Le premier argument de chaque méthode de classe, y compris
__init__, est toujours une référence à
l'instance actuelle de la classe. Par convention, cet argument est
toujours nommé self. Dans la méthode
__init__, self fait
référence à l'objet nouvellement créé, dans les autres méthodes de
classe, il fait référence à l'instance dont la méthode a été
appelée. Bien que vous deviez spécifier self
explicitement lorsque vous définissez la méthode, vous ne devez
pas le spécifier lorsque vous appelez la
méthode, Python l'ajoutera pour vous
automatiquement.
Une méthode __init__ peut prendre
n'importe quel nombre d'arguments et tout comme pour les
fonctions, les arguments peuvent être définis avec des valeurs par
défaut, ce qui les rend optionnels lors de l'appel. Ici
filename a une valeur par défaut de
None, la valeur nulle de
Python.
Par convention, le premier argument d'une méthode de classe (la
référence à l'instance en cours) est appelé self.
Cet argument remplit le rôle du mot réservé this en
C++ ou Java, mais self n'est
pas un mot réservé de Python, seulement une
convention de nommage. Cependant, veuillez ne pas l'appeler autre chose
que self, c'est une très forte convention.
Certain langage pseudo-orientés objet comme
Powerbuilder ont un concept
d'«extension» des constructeurs et autres évènements,
dans lequel la méthode de l'ancêtre est appelée automatiquement
avant que la méthode du descendant soit exécutée.
Python n'a pas ce comportement, vous
devez appeler la méthode appropriée de l'ancêtre
explicitement.
Je vous ai dit que cette classe se comportait comme un
dictionnaire, en voici le premier signe. Nous assignons l'argument
filename comme valeur de la clé
name de cet objet.
Notez que la méthode __init__ ne
retourne jamais de valeur.
5.3.2. Quand utiliser self et __init__
Lorsque vous définissez vos méthodes de classe, vous
devez indiquer explicitement
self comme premier argument de chaque méthode, y
compris __init__. Quand vous appelez une méthode
d'une classe ancêtre depuis votre classe, vous
devez inclure l'argument self.
Mais quand vous appelez votre méthode de classe de l'extérieur, vous
ne spécifiez rien pour l'argument self, vous l'omettez complètement et
Python ajoute automatiquement la référence
d'instance. Je me rends bien compte qu'on s'y perd au début, ce n'est
pas réellement incohérent même si cela peut sembler l'être car cela
est basé sur une distinction (entre méthode liée et non liée) que vous
ne connaissez pas pour l'instant.
Ouf. Je sais bien que ça fait beaucoup à absorber, mais vous ne
tarderez pas à comprendre tout ça. Toutes les classes
Python fonctionnent de la même manière, donc
quand vous en avez appris une, vous les connaissez toutes. Mais même si
vous oubliez tout le reste souvenez vous de ça, car ça vous jouera des
tours :
Les méthodes __init__ sont optionnelles,
mais quand vous en définissez une, vous devez vous rappeler d'appeler
explicitement la méthode __init__ de l'ancêtre de
la classe. C'est une règle plus générale : quand un descendant veut
étendre le comportement d'un ancêtre, la méthode du descendant doit
appeler la méthode de l'ancêtre explicitement au moment approprié,
avec les arguments appropriés.