DĂ©finition de classe dynamique en Python

La dĂ©finition d'objet dynamique peut ĂȘtre comprise comme une dĂ©finition au moment de l'exĂ©cution. Contrairement Ă  une dĂ©finition statique, qui est utilisĂ©e dans la dĂ©finition de mot-clĂ© familiĂšre d'une classe 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 areç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 typecar 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 objecthé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 objectest du type classe type. Le fait est que c'est typeune 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 typesert 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 typeinitialisé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 selfdehors 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")


compileEst 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 compileest un objet de classe code:



type(code)
<class 'code'>


L'objet codedoit ĂȘtre converti en mĂ©thode. Puisqu'une mĂ©thode est une fonction, nous commençons par convertir un codeobjet de classe en objet de classe function. Pour ce faire, importez le module types:



from types import FunctionType, MethodType


Je vais l'importer MethodTypecar 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 classe code. code.co_consts[0]Est un appel à un descripteur de co_constsclasse code, qui est un tuple avec des constantes dans le code de l'objet. Imaginez un objet codecomme un module avec une seule fonction que nous essayons d'ajouter comme méthode de classe. 0Est 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?



Liens






All Articles