class
, une définition dynamique utilise une classe en ligne type
.
Type de métaclasse
La classe de type est souvent utilisée pour obtenir le type d'un objet. Par exemple comme ceci:
h = "hello"
type(h)
<class 'str'>
Mais il a d'autres utilisations. Il peut initialiser de nouveaux types.Comme vous le savez, tout en Python est un objet. Il s'ensuit que toutes les définitions ont des types, y compris des classes et des objets. Par exemple:
class A:
pass
type(A)
<class 'type'>
Il peut ne pas ĂȘtre tout Ă fait clair pourquoi une classe se voit attribuer un type de classe
type
, par opposition Ă ses instances:
a = A()
type(a)
<class '__main__.A'>
L'objet
a
reçoit une classe en tant que type. C'est ainsi que l'interprĂ©teur traite l'objet comme une instance de la classe. La classe elle-mĂȘme a un type de classe type
car elle l'hérite de la classe de base object
:
A.__bases__
(<class 'object'>,)
Type de classe
object
:
type(object)
<class 'type'>
La classe est
object
héritée par toutes les classes par défaut, c'est-à -dire:
class A(object):
pass
Pareil que:
class A:
pass
La classe en cours de définition hérite de la classe de base en tant que type. Cependant, cela n'explique pas pourquoi la classe de base
object
est du type classe type
. Le fait est que c'est type
une métaclasse. Comme vous le savez déjà , toutes les classes héritent de la classe de base object
, qui est de type métaclasse type
. Par conséquent, toutes les classes ont également ce type, y compris la métaclasse elle type
- mĂȘme :
type(type)
<class 'type'>
C'est le "point final de la saisie" en Python. La chaßne d'héritage de type est fermée sur la classe
type
. La métaclasse type
sert de base à toutes les classes en Python. Il est facile de vérifier ceci:
builtins = [list, dict, tuple]
for obj in builtins:
type(obj)
<class 'type'>
<class 'type'>
<class 'type'>
Une classe est un type de données abstrait et ses instances ont une référence de classe comme type.
Initialisation de nouveaux types avec la classe de type
Lors de la vérification des types, la classe est
type
initialisée avec un seul argument:
type(object) -> type
Ce faisant, il renvoie le type de l'objet. Cependant, la classe implémente une méthode d'initialisation différente avec trois arguments, qui retourne un nouveau type:
type(name, bases, dict) -> new type
ParamĂštres d'initialisation de type
name
Une chaßne qui définit le nom de la nouvelle classe (type).bases
Un tuple de classes de base (classes dont la nouvelle classe héritera).dict
Dictionnaire avec les attributs de la future classe. Habituellement avec des chaßnes dans les clés et des types appelables dans les valeurs.
DĂ©finition de classe dynamique
Nous initialisons la classe du nouveau type, en fournissant tous les arguments nécessaires et l'appelons:
MyClass = type("MyClass", (object, ), dict())
MyClass
<class '__main__.MyClass'>
Vous pouvez travailler avec la nouvelle classe comme d'habitude:
m = MyClass()
m
<__main__.MyClass object at 0x7f8b1d69acc0>
De plus, la méthode est équivalente à la définition de classe habituelle:
class MyClass:
pass
DĂ©finition dynamique des attributs de classe
Il y a peu d'intĂ©rĂȘt dans une classe vide, alors la question se pose: comment ajouter des attributs et des mĂ©thodes?
Pour répondre à cette question, considérez le code d'initialisation initial:
MyClass = type(âMyClassâ, (object, ), dict())
En rĂšgle gĂ©nĂ©rale, les attributs sont ajoutĂ©s Ă la classe lors de la phase d'initialisation en tant que troisiĂšme argument - le dictionnaire. Vous pouvez spĂ©cifier des noms et des valeurs d'attribut dans le dictionnaire. Par exemple, cela pourrait ĂȘtre une variable:
MyClass = type(âMyClassâ, (object, ), dict(foo=âbarâ)
m = MyClass()
m.foo
'bar'
Définition de méthode dynamique
Les objets appelables peuvent Ă©galement ĂȘtre passĂ©s au dictionnaire, par exemple, les mĂ©thodes:
def foo(self):
return âbarâ
MyClass = type(âMyClassâ, (object, ), dict(foo=foo))
m = MyClass()
m.foo
'bar'
Cette mĂ©thode a un inconvĂ©nient important - la nĂ©cessitĂ© de dĂ©finir la mĂ©thode de maniĂšre statique (je pense que dans le contexte des tĂąches de mĂ©taprogrammation, cela peut ĂȘtre considĂ©rĂ© comme un inconvĂ©nient). De plus, la dĂ©finition d'une mĂ©thode avec un paramĂštre en
self
dehors du corps de la classe semble étrange. Par conséquent, revenons à l'initialisation dynamique d'une classe sans attributs:
MyClass = type(âMyClassâ, (object, ), dict())
AprÚs avoir initialisé une classe vide, vous pouvez y ajouter des méthodes de maniÚre dynamique, c'est-à -dire sans définition statique explicite:
code = compile('def foo(self): print(âbarâ)', "<string>", "exec")
compile
Est une fonction intĂ©grĂ©e qui compile le code source dans un objet. Le code peut ĂȘtre exĂ©cutĂ© par des fonctions exec()
ou eval()
.
Compiler les paramĂštres de fonction
- source
Le code source, peut ĂȘtre un lien vers un module. - filename
Le nom du fichier dans lequel l'objet sera compilé. - mode
Si spécifié"exec"
, la fonction compilera le code source dans un module.
Le résultat du travail
compile
est un objet de classe code
:
type(code)
<class 'code'>
L'objet
code
doit ĂȘtre converti en mĂ©thode. Puisqu'une mĂ©thode est une fonction, nous commençons par convertir un code
objet de classe en objet de classe function
. Pour ce faire, importez le module types
:
from types import FunctionType, MethodType
Je vais l'importer
MethodType
car j'en aurai besoin plus tard pour convertir la fonction en méthode de classe.
function = FunctionType(code.co_consts[0], globals(), âfooâ)
ParamÚtres de la méthode d'initialisation FunctionType
code
Objet de classecode
.code.co_consts[0]
Est un appel Ă un descripteur deco_consts
classecode
, qui est un tuple avec des constantes dans le code de l'objet. Imaginez un objetcode
comme un module avec une seule fonction que nous essayons d'ajouter comme méthode de classe.0
Est son index, puisque c'est la seule constante du module.globals()
Dictionnaire des variables globales.name
ParamÚtre facultatif spécifiant le nom de la fonction.
Le résultat est une fonction:
function
<function foo at 0x7fc79cb5ed90>
type(function)
<class 'function'>
Ensuite, vous devez ajouter cette fonction en tant que méthode de classe
MyClass
:
MyClass.foo = MethodType(function, MyClass)
Une expression assez simple qui assigne notre fonction à une méthode de classe
MyClass
.
m = MyClass()
m.foo()
bar
avertissement
Dans 99% des cas, vous pouvez vous en tirer avec des définitions de classe statiques. Cependant, le concept de métaprogrammation est bon pour révéler les éléments internes de Python. TrÚs probablement, il vous sera difficile de trouver l'application des méthodes décrites ici, bien que dans ma pratique, il y ait eu un tel cas.
Avez-vous travaillĂ© avec des objets dynamiques? Peut-ĂȘtre dans d'autres langues?