Licence CC BY-NC-ND, Thierry Parmentelat & Arnaud Legout
from IPython.display import HTML
HTML(filename="_static/style.html")
métaclasses#
on a vu dans ce chapitre, et spécifiquement ici les mécanismes généraux de l’héritage en Python
mais ici encore, c’est la version simple des choses, car en bon langae objet qui se respecte,
Python nous permet aussi .. de modifier les règles de base de l’héritage (entre autres) !
c’est le propos des métaclasses, que nous allons étudier maintenant
à quoi ça sert ?#
hum, bonne question ! en réalité on peut s’en servir pour énormément de choses! (même si, bien souvent il y a d’autres alternatives, dans la ménagerie de concepts Python, pour faire ces choses sans passer par les métaclasses…)
mais bon en tous cas c’est intéressant du point de vue conceptuel !
un prétexte: le singleton#
pour quand même travailler sur une application concrète, nous allons illustrer le concept en implémentant la notion de singleton
c’est quoi ? on parle de singleton lorsqu’on veut qu’une classe n’existe qu’en un seul exemplaire dans l’application; comme un espèce d’objet global mais qui serait proprement partagé
par exemple: la configuration; ou une classe API pour dialoguer avec un autre programme; ou bien un cache pour ranger des données…
mais avant d’en arriver là nous allons devoir faire un peu de théorie…
type et object#
jusqu’ici nous avons vu que
tout est objet en Python
tout objet a un type
une classe est un type - à partir d’ici on ne fera plus la distinction entre ces deux termes
une classe a aussi possiblement une ou plusieurs super-classes
relation entre objets et types#
du coup on peut s’intéresser à la relation binaire entre les objets:
\(X\) est une instance de \(Y\) (\(Y\) est le type de \(X\))
# et pour ça on va se créer quelques classes et objets
class Class: pass
class SubClass(Class): pass
instance = SubClass()
pour introspecter la relation “est une instance de”, c’est facile, on utilise type() !
# remontons la relation en partant de vector
type(instance), type(SubClass), type(Class), type(type)
(__main__.SubClass, type, type, type)
ce qui nous donne un premier élément de réponse:
un instance a comme type la classe qui l’a créée
une classe a comme type ..
type
on peut créer une classe avec type()
si vous êtes attentifs: on a dit qu’un objet est créé par son type (sa classe) considérée comme une usine à objets
et donc puisque la classe de Vector est type, ça signifie qu’on aurait pu créer Vector en appelant type() ?
eh bien oui, en effet !
on a souvent utilisé type() avec un seul paramètre, mais il existe aussi une forme avec 3 paramètres, qui permet de créer une classe !
ce qui nous donne le diagramme suivant
Quiz
que donne à votre avis type(int) ? et type(object)
réponse
int et object sont tous les deux des types/classes, donc leur type, c’est … type aussi
héritage entre classes#
maintenant regardons quelle est la relation entre classes et super-classes:
\(X\) hérite directement \(Y\) (\(X\) a comme classe de base \(Y\))
la question ne se pose pas à propos de vector (seules les classes ont des super-classes)
mais pour les autres on obtient ceci - en utilisant l’attribut spécial __bases__
# the base classes of our classes
SubClass.__bases__, Class.__bases__
((__main__.Class,), (object,))
# the base classes of the builtin classes
object.__bases__, type.__bases__
((), (object,))
donc on obtient, si on superpose les deux relations:
le cas d’un objet usuel#
on peut donc poursuivre notre analyse, on est toujours en train d’évaluer Class(0)
au moment d’appeler __new__ on la cherche à partir de l’objet Class
qui en général n’a pas redéfini cette méthode
on
WIP
xxx
pour en savoir plus#
cette section de la doc officielle donne tous les détails
https://docs.python.org/3/reference/datamodel.html#customizing-class-creation
comment les objets sont-ils créés ?#
une fois que ceci est bien clair, voyons un peu plus en détail comment les objets sont créés en Python
c’est-à-dire quand j’appelle par exemple
Class(0):l’objet
Classest donc un callable, on va chercher son attribut__call__comme il s’agit d’une utilisation implicite de dunder, on ne cherche pas dans l’espace de nom de
Person, mais dans sa classeor on vient de le voir, sa classe c’est
type; effectivement il existe une méthodetype.__call__()qui fait - en gros - ceci (credits)c’est-à-dire en français, on crée un objet en passant par deux étapes, qui sont chacune liée à une dunder:
__new__()une méthode de classe (car on n’a pas encore créé l’objet) dans l’esprit, elle est chargée d’allouer la mémoire__init__()une méthode usuelle chargée d’initialiser l’objet