5.4. Instantiation de classes

L'instanciation de classes en Python est simple et directe. Pour instancier une classe, appelez simplement la classe comme si elle était une fonction, en lui passant les arguments que la méthode __init__ définit. La valeur de retour sera l'objet nouvellement créé.

Exemple 5.7. Création d'une instance de FileInfo

>>> import fileinfo
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3") 1
>>> f.__class__                                        2
<class fileinfo.FileInfo at 010EC204>
>>> f.__doc__                                          3
'store file metadata'
>>> f                                                  4
{'name': '/music/_singles/kairo.mp3'}
1 Nous créons une instance de la classe FileInfo (définie dans le module fileinfo) et assignons l'instance nouvellement créée à la variable f. Nous passons un paramètre, /music/_singles/kairo.mp3, qui sera l'argument filename de la méthode __init__ de FileInfo.
2 Chaque instance de classe à un attribut prédéfini, __class__, qui est la classe de l'objet (notez que la représentation de cet attribut comprend l'adresse physique de l'instance sur ma machine, votre sortie sera différente). Les programmeurs Java sont sans doute familiers de la classe Class, qui contient des méthodes comme getName et getSuperclass permettant d'obtenir les métadonnées d'un objet. En Python, ce type de métadonnées est accessible directement par le biais de l'objet lui-même à travers des attributs comme __class__, __name__ et __bases__.
3 Vous pouvez accéder à la docstring de l'instance comme pour une fonction ou un module. Toutes les instances d'une classe partagent la même docstring.
4 Rappelez vous quand la méthode __init__ a assigné son argument filename à self["name"]. Voici le résultat. Les arguments que nous passons lorsque nous créons une instance de classe sont envoyés directement à la méthode __init__ (en même temps que la référence à l'objet, self, que Python ajoute automatiquement).
NOTE
En Python, vous appelez simplement une classe comme si c'était une fonction pour créer une nouvelle instance de la classe. Il n'y a pas d'opérateur new explicite comme pour C++ ou Java.

5.4.1. Ramasse-miettes

Si créer des instances est simple, les détruire est encore plus simple. En général, il n'y a pas besoin de libérer explicitement les instances, elles sont libérées automatiquement lorsque les variables auxquelles elles sont assignées sont hors de portée. Les fuites mémoire sont rares en Python.

Exemple 5.8. Tentative d'implémentation d'une fuite mémoire

>>> def leakmem():
...     f = fileinfo.FileInfo('/music/_singles/kairo.mp3') 1
...     
>>> for i in range(100):
...     leakmem()                                          2
1 A chaque fois que la fonction leakmem est appelée, nous créons une instance de FileInfo et l'assignons à la variable f, qui est une variable locale à la fonction. La fonction s'achève sans jamais libérer f, vous pourriez donc vous attendre à une fuite mémoire, mais vous auriez tort. Lorsque la fonction se termine, la variable locale f est hors de portée. A ce moment, il n'y a plus de référence à l'instance nouvellement créée de FileInfo (puisque nous ne l'avons jamais assignée à autre chose qu'à f), Python détruit alors l'instance pour nous.
2 Peu importe le nombre de fois que nous appelons la fonction leakmem, elle ne provoquera jamais de fuite mémoire puisque Python va détruire à chaque fois la nouvelle instance de la classe FileInfo avant le retour de leakmem.

Le terme technique pour cette forme de ramasse-miettes est «comptage de référence». Python maintien une liste des références à chaque instance créée. Dans l'exemple ci-dessus, il n'y avait qu'une référence à l'instance de FileInfo : la variable locale f. Quand la fonction se termine, la variable f sort de la portée, le compteur de référence descend alors à 0 et Python détruit l'instance automatiquement.

Dans des versions précédentes de Python, il y avait des situations où le comptage de référence échouait et Python ne pouvait pas nettoyer derrière vous. Si vous créiez deux instances qui se référençaient mutuellement (par exemple une liste doublement chaînée où chaque noeud a un pointeur vers le noeud prochain et le précédent dans la liste), aucune des deux instances n'était jamais détruite car Python pensait (correctement) qu'il y avait toujours une référence à chaque instance. Python 2.0 a une forme additionnele de ramasse-miettes appelée «mark-and-sweep» qui est assez intelligente pour remarquer ce blocage et nettoyer correctement les références circulaires.

En tant qu'ancien étudiant en Philosophie, cela me dérange de penser que les choses disparaissent quand personne ne les regarde, mais c'est exactement ce qui se passe en Python. En général, vous pouvez simplement ignorer la gestion mémoire et laisser Python nettoyer derrière vous.

Pour en savoir plus sur le ramasse-miettes