Python Language
Métaclasses
Recherche…
Introduction
Les métaclasses vous permettent de modifier en profondeur le comportement des classes Python (en termes de définition, d’instanciation, d’accès, etc.) en remplaçant la métaclasse de type
utilisée par défaut par les nouvelles classes.
Remarques
Lors de la conception de votre architecture, considérez que de nombreuses choses peuvent être accomplies avec des métaclasses en utilisant une sémantique plus simple:
- L'héritage traditionnel est souvent plus que suffisant.
- Les décorateurs de classes peuvent intégrer des fonctionnalités dans une classe selon une approche ad hoc.
- Python 3.6 introduit
__init_subclass__()
qui permet à une classe de participer à la création de sa sous-classe.
Métaclasses de base
Lorsque type
est appelé avec trois arguments, il se comporte comme la classe (méta) et crée une nouvelle instance, c.-à-d. il produit une nouvelle classe / type.
Dummy = type('OtherDummy', (), dict(x=1))
Dummy.__class__ # <type 'type'>
Dummy().__class__.__class__ # <type 'type'>
Il est possible de sous-classer le type
pour créer une métaclasse personnalisée.
class mytype(type):
def __init__(cls, name, bases, dict):
# call the base initializer
type.__init__(cls, name, bases, dict)
# perform custom initialization...
cls.__custom_attribute__ = 2
Maintenant, nous avons une nouvelle métaclasse personnalisée mytype
qui peut être utilisée pour créer des classes de la même manière que le type
.
MyDummy = mytype('MyDummy', (), dict(x=2))
MyDummy.__class__ # <class '__main__.mytype'>
MyDummy().__class__.__class__ # <class '__main__.mytype'>
MyDummy.__custom_attribute__ # 2
Lorsque nous créons une nouvelle classe en utilisant le mot-clé class
la métaclasse est choisie par défaut en fonction des classes de base.
>>> class Foo(object):
... pass
>>> type(Foo)
type
Dans l'exemple ci-dessus, la seule classe de base est object
donc notre métaclasse sera le type d' object
, qui est le type
. Il est possible de remplacer la valeur par défaut, mais cela dépend si nous utilisons Python 2 ou Python 3:
Un attribut spécial au niveau de la classe __metaclass__
peut être utilisé pour spécifier la métaclasse.
class MyDummy(object):
__metaclass__ = mytype
type(MyDummy) # <class '__main__.mytype'>
Un argument spécial de mot-clé de metaclass
spécifie la métaclasse.
class MyDummy(metaclass=mytype):
pass
type(MyDummy) # <class '__main__.mytype'>
Tout argument de mot-clé (sauf la metaclass
) dans la déclaration de classe sera transmis à la métaclasse. Ainsi, la class MyDummy(metaclass=mytype, x=2)
transmettra x=2
comme argument mot-clé au constructeur mytype
.
Lisez cette description détaillée des méta-classes de python pour plus de détails.
Singletons utilisant des métaclasses
Un singleton est un modèle qui limite l'instanciation d'une classe à une instance / objet. Pour plus d'informations sur les modèles de conception de singleton python, voir ici .
class SingletonType(type):
def __call__(cls, *args, **kwargs):
try:
return cls.__instance
except AttributeError:
cls.__instance = super(SingletonType, cls).__call__(*args, **kwargs)
return cls.__instance
class MySingleton(object):
__metaclass__ = SingletonType
class MySingleton(metaclass=SingletonType):
pass
MySingleton() is MySingleton() # True, only one instantiation occurs
Utiliser une métaclasse
La syntaxe de la métaclasse
class MyClass(object):
__metaclass__ = SomeMetaclass
class MyClass(metaclass=SomeMetaclass):
pass
Compatibilité Python 2 et 3 avec six
import six
class MyClass(six.with_metaclass(SomeMetaclass)):
pass
Fonctionnalité personnalisée avec des métaclasses
La fonctionnalité dans les métaclasses peut être modifiée de sorte que chaque fois qu'une classe est construite, une chaîne est imprimée sur la sortie standard ou une exception est levée. Cette métaclasse affichera le nom de la classe en cours de construction.
class VerboseMetaclass(type):
def __new__(cls, class_name, class_parents, class_dict):
print("Creating class ", class_name)
new_class = super().__new__(cls, class_name, class_parents, class_dict)
return new_class
Vous pouvez utiliser la métaclasse comme ceci:
class Spam(metaclass=VerboseMetaclass):
def eggs(self):
print("[insert example string here]")
s = Spam()
s.eggs()
La sortie standard sera:
Creating class Spam
[insert example string here]
Introduction aux Métaclasses
Qu'est-ce qu'une métaclasse?
En Python, tout est un objet: les entiers, les chaînes de caractères, les listes, même les fonctions et les classes elles-mêmes sont des objets. Et chaque objet est une instance d'une classe.
Pour vérifier la classe d'un objet x, on peut appeler le type(x)
, donc:
>>> type(5)
<type 'int'>
>>> type(str)
<type 'type'>
>>> type([1, 2, 3])
<type 'list'>
>>> class C(object):
... pass
...
>>> type(C)
<type 'type'>
La plupart des classes de python sont des instances de type
. type
lui-même est aussi une classe. Ces classes dont les instances sont aussi des classes sont appelées métaclasses.
La métaclasse la plus simple
OK, donc il y a déjà une métaclasse en Python: type
. Pouvons-nous en créer un autre?
class SimplestMetaclass(type):
pass
class MyClass(object):
__metaclass__ = SimplestMetaclass
Cela n'ajoute aucune fonctionnalité, mais c'est une nouvelle métaclasse, voyez que MyClass est maintenant une instance de SimplestMetaclass:
>>> type(MyClass)
<class '__main__.SimplestMetaclass'>
Une métaclasse qui fait quelque chose
Une méta - classe qui fait quelque chose remplace généralement le type
de __new__
, de modifier certaines propriétés de la classe à créer, avant d' appeler l'original __new__
qui crée la classe:
class AnotherMetaclass(type):
def __new__(cls, name, parents, dct):
# cls is this class
# name is the name of the class to be created
# parents is the list of the class's parent classes
# dct is the list of class's attributes (methods, static variables)
# here all of the attributes can be modified before creating the class, e.g.
dct['x'] = 8 # now the class will have a static variable x = 8
# return value is the new class. super will take care of that
return super(AnotherMetaclass, cls).__new__(cls, name, parents, dct)
La métaclasse par défaut
Vous avez peut-être entendu dire que tout en Python est un objet. C'est vrai, et tous les objets ont une classe:
>>> type(1)
int
Le littéral 1 est une instance de int
. Permet de déclarer une classe:
>>> class Foo(object):
... pass
...
Maintenant, permet de l'instancier:
>>> bar = Foo()
Quelle est la classe de bar
?
>>> type(bar)
Foo
Nice, le bar
est une instance de Foo
. Mais quelle est la classe de Foo
elle-même?
>>> type(Foo)
type
Ok, Foo
lui-même est une instance de type
. Qu'en est-il du type
lui-même?
>>> type(type)
type
Alors, quelle est une métaclasse? Pour l'instant, supposons que ce n'est qu'un nom de fantaisie pour la classe d'une classe. Plats à emporter:
- Tout est un objet en Python, donc tout a une classe
- La classe d'une classe s'appelle une métaclasse
- La métaclasse par défaut est
type
, et de loin c'est la métaclasse la plus courante
Mais pourquoi devriez-vous connaître les métaclasses? Eh bien, Python lui-même est assez "piratable", et le concept de métaclasse est important si vous faites des choses avancées comme la méta-programmation ou si vous voulez contrôler la façon dont vos classes sont initialisées.