En Python, que sont les métaclasses et pourquoi les utilisons-nous?
la source
En Python, que sont les métaclasses et pourquoi les utilisons-nous?
Une métaclasse est la classe d'une classe. Une classe définit le comportement d'une instance de la classe (c'est-à-dire un objet) tandis qu'une métaclasse définit le comportement d'une classe. Une classe est une instance d'une métaclasse.
Alors qu'en Python, vous pouvez utiliser des callables arbitraires pour les métaclasses (comme le montre Jerub ), la meilleure approche est d'en faire une classe réelle elle-même. type
est la métaclasse habituelle en Python. type
est lui-même une classe, et c'est son propre type. Vous ne pourrez pas recréer quelque chose comme type
purement en Python, mais Python triche un peu. Pour créer votre propre métaclasse en Python, vous voulez vraiment juste sous-classer type
.
Une métaclasse est le plus souvent utilisée comme classe-usine. Lorsque vous créez un objet en appelant la classe, Python crée une nouvelle classe (lorsqu'il exécute l'instruction 'class') en appelant la métaclasse. Combinées avec les méthodes normales __init__
et __new__
, les métaclasses vous permettent donc de faire des «choses supplémentaires» lors de la création d'une classe, comme enregistrer la nouvelle classe avec un registre ou remplacer la classe par autre chose.
Lorsque l' class
instruction est exécutée, Python exécute d'abord le corps de l' class
instruction comme un bloc de code normal. L'espace de noms résultant (un dict) contient les attributs de la future classe. La métaclasse est déterminée en examinant les classes de base de la classe à venir (les métaclasses sont héritées), l' __metaclass__
attribut de la classe à venir (le cas échéant) ou la __metaclass__
variable globale. La métaclasse est ensuite appelée avec le nom, les bases et les attributs de la classe pour l'instancier.
Cependant, les métaclasses définissent en fait le type d'une classe, pas seulement une fabrique pour celle-ci, vous pouvez donc faire beaucoup plus avec elles. Vous pouvez, par exemple, définir des méthodes normales sur la métaclasse. Ces méthodes de métaclasse sont comme des méthodes de classe en ce sens qu'elles peuvent être appelées sur la classe sans instance, mais elles ne sont pas non plus comme des méthodes de classe en ce sens qu'elles ne peuvent pas être appelées sur une instance de la classe. type.__subclasses__()
est un exemple de méthode sur la type
métaclasse. Vous pouvez également définir les méthodes «magiques» normales, comme __add__
, __iter__
et __getattr__
, pour implémenter ou modifier le comportement de la classe.
Voici un exemple agrégé des morceaux:
def make_hook(f):
"""Decorator to turn 'foo' method into '__foo__'"""
f.is_hook = 1
return f
class MyType(type):
def __new__(mcls, name, bases, attrs):
if name.startswith('None'):
return None
# Go over attributes and see if they should be renamed.
newattrs = {}
for attrname, attrvalue in attrs.iteritems():
if getattr(attrvalue, 'is_hook', 0):
newattrs['__%s__' % attrname] = attrvalue
else:
newattrs[attrname] = attrvalue
return super(MyType, mcls).__new__(mcls, name, bases, newattrs)
def __init__(self, name, bases, attrs):
super(MyType, self).__init__(name, bases, attrs)
# classregistry.register(self, self.interfaces)
print "Would register class %s now." % self
def __add__(self, other):
class AutoClass(self, other):
pass
return AutoClass
# Alternatively, to autogenerate the classname as well as the class:
# return type(self.__name__ + other.__name__, (self, other), {})
def unregister(self):
# classregistry.unregister(self)
print "Would unregister class %s now." % self
class MyObject:
__metaclass__ = MyType
class NoneSample(MyObject):
pass
# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)
class Example(MyObject):
def __init__(self, value):
self.value = value
@make_hook
def add(self, other):
return self.__class__(self.value + other.value)
# Will unregister the class
Example.unregister()
inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()
print inst + inst
class Sibling(MyObject):
pass
ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__
class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b
__metaclass__
n'est pas pris en charge dans Python 3. Dans l'utilisation de Python 3class MyObject(metaclass=MyType)
, voir python.org/dev/peps/pep-3115 et la réponse ci-dessous.Classes en tant qu'objets
Avant de comprendre les métaclasses, vous devez maîtriser les classes en Python. Et Python a une idée très particulière de ce que sont les classes, empruntée au langage Smalltalk.
Dans la plupart des langages, les classes ne sont que des morceaux de code qui décrivent comment produire un objet. C'est un peu vrai aussi en Python:
Mais les classes sont plus que cela en Python. Les classes sont aussi des objets.
Oui, des objets.
Dès que vous utilisez le mot-clé
class
, Python l'exécute et crée un OBJET. L'instructioncrée en mémoire un objet avec le nom "ObjectCreator".
Cet objet (la classe) est lui-même capable de créer des objets (les instances), et c'est pourquoi c'est une classe .
Mais quand même, c'est un objet, et donc:
par exemple:
Création dynamique de classes
Les classes étant des objets, vous pouvez les créer à la volée, comme n'importe quel objet.
Tout d'abord, vous pouvez créer une classe dans une fonction en utilisant
class
:Mais ce n'est pas si dynamique, car vous devez encore écrire toute la classe vous-même.
Les classes étant des objets, elles doivent être générées par quelque chose.
Lorsque vous utilisez le
class
mot clé, Python crée automatiquement cet objet. Mais comme avec la plupart des choses en Python, cela vous donne un moyen de le faire manuellement.Rappelez-vous la fonction
type
? La bonne vieille fonction qui vous permet de savoir de quel type est un objet:Eh bien,
type
a une capacité complètement différente, il peut également créer des classes à la volée.type
peut prendre la description d'une classe comme paramètres et renvoyer une classe.(Je sais, c'est idiot que la même fonction puisse avoir deux utilisations complètement différentes selon les paramètres que vous lui passez. C'est un problème en raison de la compatibilité descendante en Python)
type
fonctionne de cette façon:Où:
name
: nom de la classebases
: tuple de la classe parent (pour l'héritage, peut être vide)attrs
: dictionnaire contenant les noms et valeurs des attributspar exemple:
peut être créé manuellement de cette façon:
Vous remarquerez que nous utilisons "MyShinyClass" comme nom de la classe et comme variable pour contenir la référence de classe. Ils peuvent être différents, mais il n'y a aucune raison de compliquer les choses.
type
accepte un dictionnaire pour définir les attributs de la classe. Donc:Peut être traduit en:
Et utilisé comme classe normale:
Et bien sûr, vous pouvez en hériter, donc:
serait:
Finalement, vous voudrez ajouter des méthodes à votre classe. Définissez simplement une fonction avec la signature appropriée et affectez-la comme attribut.
Et vous pouvez ajouter encore plus de méthodes après avoir créé dynamiquement la classe, tout comme l'ajout de méthodes à un objet de classe normalement créé.
Vous voyez où nous allons: en Python, les classes sont des objets, et vous pouvez créer une classe à la volée, dynamiquement.
C'est ce que fait Python lorsque vous utilisez le mot clé
class
, et il le fait en utilisant une métaclasse.Que sont les métaclasses (enfin)
Les métaclasses sont le «truc» qui crée des classes.
Vous définissez des classes afin de créer des objets, non?
Mais nous avons appris que les classes Python sont des objets.
Eh bien, les métaclasses sont ce qui crée ces objets. Ce sont les classes des classes, vous pouvez les imaginer de cette façon:
Vous avez vu que cela
type
vous permet de faire quelque chose comme ceci:C'est parce que la fonction
type
est en fait une métaclasse.type
est la métaclasse utilisée par Python pour créer toutes les classes dans les coulisses.Vous vous demandez maintenant pourquoi diable est-il écrit en minuscules et non
Type
?Eh bien, je suppose que c'est une question de cohérence avec
str
, la classe qui crée des objets chaînes etint
la classe qui crée des objets entiers.type
est juste la classe qui crée des objets de classe.Vous voyez cela en vérifiant l'
__class__
attribut.Tout, et je veux dire tout, est un objet en Python. Cela inclut les entiers, les chaînes, les fonctions et les classes. Ce sont tous des objets. Et tous ont été créés à partir d'une classe:
Maintenant, quel est le
__class__
problème__class__
?Ainsi, une métaclasse est juste ce qui crée des objets de classe.
Vous pouvez l'appeler une «usine de classe» si vous le souhaitez.
type
est la métaclasse intégrée utilisée par Python, mais bien sûr, vous pouvez créer votre propre métaclasse.L'
__metaclass__
attributEn Python 2, vous pouvez ajouter un
__metaclass__
attribut lorsque vous écrivez une classe (voir la section suivante pour la syntaxe Python 3):Si vous le faites, Python utilisera la métaclasse pour créer la classe
Foo
.Attention, c'est délicat.
Vous écrivez d'
class Foo(object)
abord, mais l'objet de classeFoo
n'est pas encore créé en mémoire.Python recherchera
__metaclass__
dans la définition de classe. S'il le trouve, il l'utilisera pour créer la classe d'objetsFoo
. Si ce n'est pas le cas, il utiliseratype
pour créer la classe.Lisez cela plusieurs fois.
Quand vous faites:
Python fait ce qui suit:
Y a-t-il un
__metaclass__
attribut dansFoo
?Si oui, créez en mémoire un objet de classe (j'ai dit un objet de classe, restez avec moi ici), avec le nom
Foo
en utilisant ce qui est dedans__metaclass__
.Si Python ne peut pas trouver
__metaclass__
, il recherchera un__metaclass__
au niveau MODULE, et essaiera de faire la même chose (mais seulement pour les classes qui n'héritent de rien, essentiellement des classes à l'ancienne).S'il n'en trouve pas
__metaclass__
du tout, il utilisera laBar
propre métaclasse de (le premier parent) (qui pourrait être la valeur par défauttype
) pour créer l'objet de classe.Attention ici que l'
__metaclass__
attribut ne sera pas hérité, la métaclasse du parent (Bar.__class__
) le sera. SiBar
utilisé un__metaclass__
attribut crééBar
avectype()
(et nontype.__new__()
), les sous-classes n'hériteront pas de ce comportement.Maintenant, la grande question est, que pouvez-vous y mettre
__metaclass__
?La réponse est: quelque chose qui peut créer une classe.
Et qu'est-ce qui peut créer une classe?
type
ou tout ce qui le sous-classe ou l'utilise.Métaclasses dans Python 3
La syntaxe pour définir la métaclasse a été modifiée dans Python 3:
c'est-à-dire que l'
__metaclass__
attribut n'est plus utilisé, en faveur d'un argument mot-clé dans la liste des classes de base.Le comportement des métaclasses reste cependant sensiblement le même .
Une chose ajoutée aux métaclasses en python 3 est que vous pouvez également passer des attributs en tant qu'arguments de mot-clé dans une métaclasse, comme ceci:
Lisez la section ci-dessous pour savoir comment python gère cela.
Métaclasses personnalisées
Le but principal d'une métaclasse est de changer automatiquement la classe lors de sa création.
Vous le faites généralement pour les API, où vous souhaitez créer des classes correspondant au contexte actuel.
Imaginez un exemple stupide, où vous décidez que toutes les classes de votre module devraient avoir leurs attributs écrits en majuscules. Il existe plusieurs façons de procéder, mais l'une d'elles consiste à définir
__metaclass__
au niveau du module.De cette façon, toutes les classes de ce module seront créées à l'aide de cette métaclasse, et nous n'avons qu'à dire à la métaclasse de transformer tous les attributs en majuscules.
Heureusement, il
__metaclass__
peut en fait être appelable, il n'a pas besoin d'être une classe formelle (je sais, quelque chose avec «classe» dans son nom n'a pas besoin d'être une classe, allez comprendre ... mais c'est utile).Nous allons donc commencer par un exemple simple, en utilisant une fonction.
Allons vérifier:
Maintenant, faisons exactement la même chose, mais en utilisant une vraie classe pour une métaclasse:
Réécrivons ce qui précède, mais avec des noms de variables plus courts et plus réalistes maintenant que nous savons ce qu'ils signifient:
Vous avez peut-être remarqué l'argument supplémentaire
cls
. Il n'y a rien de spécial:__new__
reçoit toujours la classe dans laquelle il est défini, comme premier paramètre. Tout comme vous l'avezself
pour les méthodes ordinaires qui reçoivent l'instance comme premier paramètre, ou la classe de définition pour les méthodes de classe.Mais ce n'est pas la POO appropriée. Nous appelons
type
directement et nous ne remplaçons ni n'appelons les parents__new__
. Faisons cela à la place:Nous pouvons le rendre encore plus propre en utilisant
super
, ce qui facilitera l'héritage (car oui, vous pouvez avoir des métaclasses, héritant des métaclasses, héritant du type):Oh, et en python 3 si vous faites cet appel avec des arguments de mots clés, comme ceci:
Cela se traduit par ceci dans la métaclasse pour l'utiliser:
C'est ça. Il n'y a vraiment plus rien sur les métaclasses.
La raison de la complexité du code utilisant des métaclasses n'est pas à cause des métaclasses, c'est parce que vous utilisez généralement des métaclasses pour faire des trucs tordus en s'appuyant sur l'introspection, en manipulant l'héritage, des variables telles que
__dict__
, etc.En effet, les métaclasses sont particulièrement utiles pour faire de la magie noire, et donc des trucs compliqués. Mais par eux-mêmes, ils sont simples:
Pourquoi utiliseriez-vous des classes de métaclasses au lieu de fonctions?
Puisque
__metaclass__
peut accepter n'importe quel appelable, pourquoi utiliseriez-vous une classe car elle est évidemment plus compliquée?Il y a plusieurs raisons de le faire:
UpperAttrMetaclass(type)
, vous savez ce qui va suivre__new__
,__init__
et__call__
. Ce qui vous permettra de faire différentes choses. Même si vous pouvez généralement tout faire__new__
, certaines personnes sont tout simplement plus à l'aise__init__
.Pourquoi utiliseriez-vous des métaclasses?
Maintenant, la grande question. Pourquoi utiliseriez-vous une fonction obscure sujette aux erreurs?
Eh bien, en général, vous ne le faites pas:
Python Guru Tim Peters
Le cas d'utilisation principal d'une métaclasse est la création d'une API. Un exemple typique de ceci est l'ORM Django. Il vous permet de définir quelque chose comme ceci:
Mais si vous faites cela:
Il ne retournera pas d'
IntegerField
objet. Il renverra unint
, et peut même le prendre directement à partir de la base de données.Ceci est possible car
models.Model
définit__metaclass__
et utilise de la magie qui transformera le quePerson
vous venez de définir avec des instructions simples en un crochet complexe vers un champ de base de données.Django rend quelque chose de complexe simple en exposant une API simple et en utilisant des métaclasses, en recréant du code à partir de cette API pour faire le vrai travail dans les coulisses.
Le dernier mot
Tout d'abord, vous savez que les classes sont des objets qui peuvent créer des instances.
Et bien en fait, les classes sont elles-mêmes des instances. De métaclasses.
Tout est un objet en Python, et ce sont tous des instances de classes ou des instances de métaclasses.
Sauf pour
type
.type
est en fait sa propre métaclasse. Ce n'est pas quelque chose que vous pourriez reproduire en Python pur, et cela se fait en trichant un peu au niveau de l'implémentation.Deuxièmement, les métaclasses sont compliquées. Vous ne voudrez peut-être pas les utiliser pour des modifications de classe très simples. Vous pouvez changer de classe en utilisant deux techniques différentes:
99% du temps où vous avez besoin d'une modification de classe, il vaut mieux les utiliser.
Mais 98% du temps, vous n'avez pas du tout besoin de modification de classe.
la source
models.Model
il n'utilise pas__metaclass__
mais plutôtclass Model(metaclass=ModelBase):
pour référencer uneModelBase
classe qui fait alors la magie de métaclasse susmentionnée. Super article! Voici la source Django: github.com/django/django/blob/master/django/db/models/…__metaclass__
attribut ne sera pas hérité, la métaclasse du parent (Bar.__class__
) le sera. SiBar
utilisé un__metaclass__
attribut crééBar
avectype()
(et nontype.__new__()
), les sous-classes n'hériteront pas de ce comportement. >> - Pourriez-vous / quelqu'un expliquer un peu plus en profondeur ce passage?Now you wonder why the heck is it written in lowercase, and not Type?
- bien parce qu'il est implémenté en C - c'est la même raison pour laquelle defaultdict est en minuscule tandis que OrderedDict (en python 2) est normal CamelCaseRemarque, cette réponse est pour Python 2.x comme il a été écrit en 2008, les métaclasses sont légèrement différentes dans 3.x.
Les métaclasses sont la sauce secrète qui fait fonctionner la «classe». La métaclasse par défaut pour un nouvel objet de style est appelée «type».
Les métaclasses prennent 3 arguments. « nom », « bases » et « dict »
Voici où commence le secret. Recherchez d'où proviennent le nom, les bases et le dict dans cet exemple de définition de classe.
Permet de définir une métaclasse qui montrera comment « classe: » l'appelle.
Et maintenant, un exemple qui signifie réellement quelque chose, cela rendra automatiquement les variables de la liste "attributs" définies sur la classe, et définies sur Aucune.
Notez que le comportement magique qui
Initialised
gagne en ayant la métaclasseinit_attributes
n'est pas transmis à une sous-classe deInitialised
.Voici un exemple encore plus concret, montrant comment vous pouvez sous-classer «type» pour créer une métaclasse qui exécute une action lors de la création de la classe. C'est assez délicat:
la source
D'autres ont expliqué comment fonctionnent les métaclasses et comment elles s'intègrent dans le système de type Python. Voici un exemple de leur utilisation. Dans un cadre de test que j'ai écrit, je voulais garder une trace de l'ordre dans lequel les classes étaient définies, afin de pouvoir les instancier plus tard dans cet ordre. J'ai trouvé plus facile de le faire en utilisant une métaclasse.
Tout ce qui est une sous-classe
MyType
obtient alors un attribut de classe_order
qui enregistre l'ordre dans lequel les classes ont été définies.la source
__init__(self)
dittype(self)._order = MyBase.counter; MyBase.counter += 1
?L'une des utilisations des métaclasses consiste à ajouter automatiquement de nouvelles propriétés et méthodes à une instance.
Par exemple, si vous regardez les modèles Django , leur définition semble un peu déroutante. Il semble que vous ne définissiez que les propriétés de classe:
Cependant, lors de l'exécution, les objets Person sont remplis de toutes sortes de méthodes utiles. Voir la source pour une métaclasserie incroyable.
la source
Je pense que l'introduction d'ONLamp à la programmation des métaclasses est bien écrite et donne une très bonne introduction au sujet malgré plusieurs années déjà.
http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html (archivé sur https://web.archive.org/web/20080206005253/http://www.onlamp. com / pub / a / python / 2003/04/17 / metaclasses.html )
En bref: une classe est un plan pour la création d'une instance, une métaclasse est un plan pour la création d'une classe. On peut facilement voir qu'en Python, les classes doivent également être des objets de première classe pour activer ce comportement.
Je n'en ai jamais écrit moi-même, mais je pense que l'une des plus belles utilisations des métaclasses peut être vue dans le framework Django . Les classes de modèle utilisent une approche de métaclasse pour permettre un style déclaratif d'écriture de nouveaux modèles ou classes de formulaire. Pendant que la métaclasse crée la classe, tous les membres ont la possibilité de personnaliser la classe elle-même.
La chose qui reste à dire est: si vous ne savez pas ce que sont les métaclasses, la probabilité que vous n'en ayez pas besoin est de 99%.
la source
TLDR: une métaclasse instancie et définit le comportement d'une classe tout comme une classe instancie et définit le comportement d'une instance.
Pseudocode:
Ce qui précède devrait vous sembler familier. Eh bien, d'où
Class
vient-il? C'est une instance d'une métaclasse (également pseudocode):En vrai code, nous pouvons passer la métaclasse par défaut
type
, tout ce dont nous avons besoin pour instancier une classe et nous obtenons une classe:Autrement dit
Une classe est à une instance comme une métaclasse est à une classe.
Lorsque nous instancions un objet, nous obtenons une instance:
De même, lorsque nous définissons une classe explicitement avec la métaclasse par défaut
type
, nous l'instancions:Autrement dit, une classe est une instance d'une métaclasse:
Autrement dit, une métaclasse est la classe d'une classe.
Lorsque vous écrivez une définition de classe et que Python l'exécute, il utilise une métaclasse pour instancier l'objet de classe (qui sera à son tour utilisé pour instancier des instances de cette classe).
Tout comme nous pouvons utiliser des définitions de classe pour modifier le comportement des instances d'objet personnalisé, nous pouvons utiliser une définition de classe de métaclasse pour modifier le comportement d'un objet de classe.
À quoi peuvent-ils servir? De la documentation :
Néanmoins, il est généralement recommandé aux utilisateurs d'éviter d'utiliser des métaclasses, sauf en cas d'absolue nécessité.
Vous utilisez une métaclasse chaque fois que vous créez une classe:
Lorsque vous écrivez une définition de classe, par exemple, comme ceci,
Vous instanciez un objet de classe.
Cela revient à appeler fonctionnellement
type
avec les arguments appropriés et à affecter le résultat à une variable de ce nom:Notez que certaines choses sont automatiquement ajoutées à
__dict__
l'espace de noms, c'est-à-dire:La métaclasse de l'objet que nous avons créé, dans les deux cas, est
type
.(Une note latérale sur le contenu de la classe
__dict__
:__module__
est là parce que les classes doivent savoir où elles sont définies,__dict__
et__weakref__
sont là parce que nous ne définissons pas__slots__
- si nous définissons__slots__
nous économiserons un peu d'espace dans les instances, comme nous pouvons les refuser__dict__
et les__weakref__
exclure. Par exemple:... mais je m'égare.)
Nous pouvons étendre
type
comme toute autre définition de classe:Voici la valeur par défaut
__repr__
des classes:L'une des choses les plus précieuses que nous pouvons faire par défaut en écrivant un objet Python est de lui fournir un bien
__repr__
. Lorsque nous appelons,help(repr)
nous apprenons qu'il existe un bon test pour un__repr__
qui nécessite également un test d'égalité -obj == eval(repr(obj))
. L'implémentation simple suivante de__repr__
et__eq__
pour les instances de classe de notre classe de type nous fournit une démonstration qui peut améliorer la valeur par défaut__repr__
des classes:Alors maintenant, quand nous créons un objet avec cette métaclasse, l'
__repr__
écho sur la ligne de commande offre une vue beaucoup moins laide que la valeur par défaut:Avec une belle
__repr__
définition pour l'instance de classe, nous avons une plus grande capacité à déboguer notre code. Cependant, une vérification plus approfondie aveceval(repr(Class))
est peu probable (car les fonctions seraient plutôt impossibles à évaluer par rapport à celles par défaut__repr__
).Une utilisation attendue:
__prepare__
un espace de nomsSi, par exemple, nous voulons savoir dans quel ordre les méthodes d'une classe sont créées, nous pouvons fournir un dict ordonné comme espace de noms de la classe. Nous ferions cela avec
__prepare__
lequel retourne le dict d'espace de noms pour la classe s'il est implémenté en Python 3 :Et l'utilisation:
Et maintenant, nous avons un enregistrement de l'ordre dans lequel ces méthodes (et d'autres attributs de classe) ont été créés:
Remarque, cet exemple a été adapté de la documentation - la nouvelle énumération de la bibliothèque standard le fait.
Nous avons donc instancié une métaclasse en créant une classe. Nous pouvons également traiter la métaclasse comme nous le ferions pour n'importe quelle autre classe. Il a un ordre de résolution de méthode:
Et il a approximativement la bonne
repr
(que nous ne pouvons plus évaluer à moins que nous ne trouvions un moyen de représenter nos fonctions.):la source
Mise à jour Python 3
Il existe (à ce stade) deux méthodes clés dans une métaclasse:
__prepare__
, et__new__
__prepare__
vous permet de fournir un mappage personnalisé (tel qu'unOrderedDict
) à utiliser comme espace de noms pendant la création de la classe. Vous devez renvoyer une instance de l'espace de noms que vous choisissez. Si vous n'implémentez pas__prepare__
un normaldict
est utilisé.__new__
est responsable de la création / modification effective de la classe finale.Une métaclasse nue et sans rien faire aimerait:
Un exemple simple:
Supposons que vous vouliez qu'un code de validation simple s'exécute sur vos attributs - comme s'il devait toujours être un
int
ou unstr
. Sans métaclasse, votre classe ressemblerait à quelque chose comme:Comme vous pouvez le voir, vous devez répéter deux fois le nom de l'attribut. Cela rend les fautes de frappe possibles avec des bugs irritants.
Une simple métaclasse peut résoudre ce problème:
Voici à quoi ressemblerait la métaclasse (ne pas utiliser
__prepare__
car elle n'est pas nécessaire):Un échantillon de:
produit:
Remarque : Cet exemple est assez simple, il aurait également pu être réalisé avec un décorateur de classe, mais vraisemblablement une métaclasse réelle ferait beaucoup plus.
La classe 'ValidateType' pour référence:
la source
__set_name__(cls, name)
dans le descripteur (ValidateType
) pour définir le nom dans le descripteur (self.name
et dans ce cas égalementself.attr
). Cela a été ajouté pour ne pas avoir à plonger dans les métaclasses pour ce cas d'utilisation courant spécifique (voir PEP 487).Rôle d'une
__call__()
méthode de métaclasse lors de la création d'une instance de classeSi vous avez fait de la programmation Python pendant plus de quelques mois, vous finirez par tomber sur du code qui ressemble à ceci:
Ce dernier est possible lorsque vous implémentez la
__call__()
méthode magique sur la classe.La
__call__()
méthode est invoquée lorsqu'une instance d'une classe est utilisée comme appelable. Mais comme nous l'avons vu dans les réponses précédentes, une classe elle-même est une instance d'une métaclasse, donc lorsque nous utilisons la classe comme appelable (c'est-à-dire lorsque nous en créons une instance), nous appelons en fait sa__call__()
méthode de métaclasse . À ce stade, la plupart des programmeurs Python sont un peu confus car on leur a dit que lors de la création d'une instance comme celle-ci,instance = SomeClass()
vous appelez sa__init__()
méthode. Certains de ceux qui ont creusé un peu plus profond savent que avant__init__()
il y a__new__()
. Eh bien, aujourd'hui, une autre couche de vérité est révélée, avant__new__()
la métaclasse »__call__()
.Étudions la chaîne d'appels de méthode dans la perspective spécifique de la création d'une instance d'une classe.
Il s'agit d'une métaclasse qui se connecte exactement au moment où une instance est créée et au moment où elle est sur le point de la retourner.
Ceci est une classe qui utilise cette métaclasse
Et maintenant, créons une instance de
Class_1
Notez que le code ci-dessus ne fait rien de plus que la journalisation des tâches. Chaque méthode délègue le travail réel à l'implémentation de son parent, conservant ainsi le comportement par défaut. Étant donné que
type
c'estMeta_1
la classe parent (type
étant la métaclasse parent par défaut) et compte tenu de la séquence de classement de la sortie ci-dessus, nous avons maintenant un indice sur ce que serait la pseudo implémentation detype.__call__()
:Nous pouvons voir que la
__call__()
méthode de la métaclasse est celle qui est appelée en premier. Il délègue ensuite la création de l'instance à la__new__()
méthode de la classe et l'initialisation à celle de l'instance__init__()
. C'est aussi celui qui renvoie finalement l'instance.De ce qui précède, il ressort que la métaclasse 'a
__call__()
également la possibilité de décider si un appel àClass_1.__new__()
ouClass_1.__init__()
sera éventuellement effectué. Au cours de son exécution, il pourrait en fait renvoyer un objet qui n'a été touché par aucune de ces méthodes. Prenons par exemple cette approche du modèle singleton:Observons ce qui se passe lorsque l'on essaie à plusieurs reprises de créer un objet de type
Class_2
la source
Une métaclasse est une classe qui indique comment (certaines) autres classes doivent être créées.
C'est un cas où j'ai vu la métaclasse comme une solution à mon problème: j'avais un problème vraiment compliqué, qui aurait probablement pu être résolu différemment, mais j'ai choisi de le résoudre en utilisant une métaclasse. En raison de la complexité, c'est l'un des rares modules que j'ai écrits où les commentaires dans le module dépassent la quantité de code qui a été écrite. C'est ici...
la source
La version tl; dr
La
type(obj)
fonction vous obtient le type d'un objet.Le
type()
d'une classe est sa métaclasse .Pour utiliser une métaclasse:
type
est sa propre métaclasse. La classe d'une classe est une métaclasse - le corps d'une classe est les arguments passés à la métaclasse qui est utilisée pour construire la classe.Ici, vous pouvez lire comment utiliser les métaclasses pour personnaliser la construction des classes.
la source
type
est en fait unemetaclass
- une classe qui crée une autre classe. La plupartmetaclass
sont des sous-classes detype
. Lemetaclass
reçoit lanew
classe comme premier argument et donne accès à l'objet classe avec les détails mentionnés ci-dessous:Note:
Notez que la classe n'a été instanciée à aucun moment; le simple fait de créer la classe a déclenché l'exécution du
metaclass
.la source
Les classes Python sont elles-mêmes des objets - comme par exemple - de leur méta-classe.
La métaclasse par défaut, qui est appliquée lorsque vous déterminez des classes comme:
les méta-classes sont utilisées pour appliquer une règle à un ensemble complet de classes. Par exemple, supposons que vous construisez un ORM pour accéder à une base de données et que vous souhaitiez que les enregistrements de chaque table appartiennent à une classe mappée à cette table (en fonction des champs, des règles métier, etc.), une utilisation possible de la métaclasse est par exemple, la logique du pool de connexions, qui est partagée par toutes les classes d'enregistrement de toutes les tables. Une autre utilisation est la logique pour prendre en charge les clés étrangères, ce qui implique plusieurs classes d'enregistrements.
lorsque vous définissez la métaclasse, vous tapez le type de sous-classe et pouvez remplacer les méthodes magiques suivantes pour insérer votre logique.
de toute façon, ces deux sont les crochets les plus couramment utilisés. le métaclassage est puissant, et la liste des utilisations du métaclassage est loin d'être exhaustive et exhaustive.
la source
La fonction type () peut renvoyer le type d'un objet ou créer un nouveau type,
par exemple, nous pouvons créer une classe Hi avec la fonction type () et nous n'avons pas besoin de l'utiliser de cette façon avec la classe Hi (objet):
En plus d'utiliser type () pour créer des classes dynamiquement, vous pouvez contrôler le comportement de création de classe et utiliser la métaclasse.
Selon le modèle d'objet Python, la classe est l'objet, donc la classe doit être une instance d'une autre certaine classe. Par défaut, une classe Python est une instance de la classe type. Autrement dit, le type est la métaclasse de la plupart des classes intégrées et la métaclasse des classes définies par l'utilisateur.
La magie prendra effet lorsque nous passerons des arguments de mots clés dans la métaclasse, elle indique à l'interpréteur Python de créer la CustomList via ListMetaclass. new (), à ce stade, nous pouvons modifier la définition de classe, par exemple, et ajouter une nouvelle méthode, puis renvoyer la définition révisée.
la source
En plus des réponses publiées, je peux dire que a
metaclass
définit le comportement d'une classe. Ainsi, vous pouvez définir explicitement votre métaclasse. Chaque fois que Python obtient un mot-clé,class
il commence à rechercher lemetaclass
. S'il n'est pas trouvé - le type de métaclasse par défaut est utilisé pour créer l'objet de la classe. En utilisant l'__metaclass__
attribut, vous pouvez définirmetaclass
votre classe:Cela produira la sortie comme ceci:
Et, bien sûr, vous pouvez créer le vôtre
metaclass
pour définir le comportement de toute classe créée à l'aide de votre classe.Pour ce faire, votre
metaclass
classe de type par défaut doit être héritée car c'est le principalmetaclass
:La sortie sera:
la source
En programmation orientée objet, une métaclasse est une classe dont les instances sont des classes. Tout comme une classe ordinaire définit le comportement de certains objets, une métaclasse définit le comportement de certaines classes et de leurs instances Le terme métaclasse signifie simplement quelque chose utilisé pour créer des classes. En d'autres termes, c'est la classe d'une classe. La métaclasse est utilisée pour créer la classe. Ainsi, comme l'objet étant une instance d'une classe, une classe est une instance d'une métaclasse. En python, les classes sont également considérées comme des objets.
la source
Voici un autre exemple de son utilisation:
metaclass
pour changer la fonction de son instance (la classe).Le
metaclass
est puissant, il y a beaucoup de choses (comme la magie des singes) que vous pouvez faire avec, mais attention cela ne peut être connu que de vous.la source
Une classe, en Python, est un objet, et comme tout autre objet, c'est une instance de "quelque chose". Ce "quelque chose" est ce qu'on appelle une métaclasse. Cette métaclasse est un type spécial de classe qui crée des objets d'autres classes. Par conséquent, la métaclasse est responsable de la création de nouvelles classes. Cela permet au programmeur de personnaliser la façon dont les classes sont générées.
Pour créer une métaclasse, le remplacement des méthodes new () et init () est généralement effectué. new () peut être surchargé pour changer la façon dont les objets sont créés, tandis que init () peut être surchargé pour changer la façon d'initialiser l'objet. La métaclasse peut être créée de plusieurs façons. L'une des façons consiste à utiliser la fonction type (). La fonction type (), lorsqu'elle est appelée avec 3 paramètres, crée une métaclasse. Les paramètres sont: -
Une autre façon de créer une métaclasse consiste à utiliser le mot clé «métaclasse». Définissez la métaclasse comme une classe simple. Dans les paramètres de la classe héritée, passez metaclass = metaclass_name
La métaclasse peut être spécifiquement utilisée dans les situations suivantes: -
la source
Notez que dans python 3.6, une nouvelle méthode dunder a
__init_subclass__(cls, **kwargs)
été introduite pour remplacer de nombreux cas d'utilisation courants pour les métaclasses. Is est appelée lorsqu'une sous-classe de la classe de définition est créée. Voir les documents python .la source
La métaclasse est une sorte de classe qui définit comment la classe se comportera ou on peut dire qu'une classe est elle-même une instance d'une métaclasse.
la source