Question

Aujourd'hui, je suis venu à travers une définition étonnante d'un métaclasse en Python , avec la définition de métaclasse efficacement inline. La partie pertinente est

class Plugin(object):
    class __metaclass__(type):
        def __init__(cls, name, bases, dict):
            type.__init__(name, bases, dict)
            registry.append((name, cls))

Quand est-il logique d'utiliser une telle définition en ligne?

D'autres arguments:

Un argument d'une façon serait que le métaclasse créé n'est pas réutilisable ailleurs en utilisant cette technique. Un contre-argument est qu'un motif commun en utilisant des métaclasses définit une métaclasse et l'utiliser dans une classe et inhertiting puis à partir de cela. Par exemple, dans une métaclasse conservatrice la définition

class DeclarativeMeta(type):
    def __new__(meta, class_name, bases, new_attrs):
        cls = type.__new__(meta, class_name, bases, new_attrs)
        cls.__classinit__.im_func(cls, new_attrs)
        return cls
class Declarative(object):
    __metaclass__ = DeclarativeMeta
    def __classinit__(cls, new_attrs): pass

aurait pu être écrit comme

class Declarative(object):  #code not tested!
    class __metaclass__(type):
        def __new__(meta, class_name, bases, new_attrs):
            cls = type.__new__(meta, class_name, bases, new_attrs)
            cls.__classinit__.im_func(cls, new_attrs)
            return cls
    def __classinit__(cls, new_attrs): pass

Toutes les autres considérations?

Était-ce utile?

La solution

Comme toute autre forme de définition de classe imbriquée, une méta-classe imbriquée peut être plus « compact et pratique » (aussi longtemps que vous êtes OK avec ne pas réutiliser que métaclasse sauf par héritage) pour de nombreux types d ' « utilisation de la production », mais peut être un peu gênant pour le débogage et l'introspection.

En fait, au lieu de donner la métaclasse un bon, le nom de niveau supérieur, vous allez finir avec tous métaclasses personnalisés définis dans un module étant undistiguishable les uns des autres en fonction de leurs attributs de __module__ et __name__ (qui est ce que Python utilisations pour former leur repr si nécessaire). Considérez:

>>> class Mcl(type): pass
... 
>>> class A: __metaclass__ = Mcl
...
>>> class B:
...   class __metaclass__(type): pass
... 
>>> type(A)
<class '__main__.Mcl'>
>>> type(B)
<class '__main__.__metaclass__'>

OIEau, si vous voulez examiner « quel type est la classe A » (un méta-classe est le type de la classe, rappelez-vous), vous obtenez une réponse claire et utile - il est Mcl dans le module principal. Toutefois, si vous voulez examiner « quel type est la classe B », la réponse est d'une grande utilité: il dit il de __metaclass__ dans le module main, mais c'est même pas vrai:

>>> import __main__
>>> __main__.__metaclass__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__metaclass__'
>>> 

... il est pas une telle chose, en fait; que rééd est trompeur et pas très utile; -).

La rééd d'une classe est essentiellement '%s.%s' % (c.__module__, c.__name__) - un simple, utile et règle cohérente - mais dans de nombreux cas tels que la déclaration de class pas être unique à la portée du module, ou étant pas du champ d'application du module du tout (mais plutôt dans une fonction ou corps classe), ou même pas existant (les classes peuvent bien sûr être construit sans une déclaration de class, en appelant explicitement leur métaclasse), cela peut être quelque peu trompeur (et la meilleure solution est d'éviter, dans la mesure du possible, ces cas particuliers, sauf si un avantage substantiel peut être obtenu en les utilisant). Par exemple, considérons:

>>> class A(object):
...   def foo(self): print('first')
... 
>>> x = A()
>>> class A(object):
...   def foo(self): print('second')
... 
>>> y = A()
>>> x.foo()
first
>>> y.foo()
second
>>> x.__class__
<class '__main__.A'>
>>> y.__class__
<class '__main__.A'>
>>> x.__class__ is y.__class__
False

avec deux déclaration class à la même portée, le second lie de nouveau le nom (ici, A), mais les instances existantes se réfèrent à la première liaison du nom par objet, non pas par son nom - afin que les deux objets de classe restent, une accessible uniquement par l'type (ou attribut __class__) de ses instances (le cas échéant - si aucune, ce premier objet disparaît) - les deux classes ont le même nom et le module (et donc la même représentation), mais ils sont distincts objets. Les classes imbriquées au sein des instances de classe ou fonction, ou créées en appelant directement la métaclasse (y compris type), peut entraîner une confusion similaire si le débogage ou l'introspection est toujours appelé.

Ainsi, l'imbrication de la métaclasse est OK si vous aurez jamais besoin de debug ou autrement Introspect ce code, et peut vivre avec si celui qui est tellement fait comprendre ce bizarreries (bien qu'il ne sera jamais aussi pratique que l'utilisation d'un bien, nom, bien sûr - comme le débogage d'une fonction codée avec lambda ne peut-être jamais être aussi pratique que le débogage d'un code avec def). Par analogie avec lambda vs def vous pouvez raisonnablement prétendre que l'anonymat, la définition « imbriquée » est OK pour métaclasses qui sont si tout à fait simples, sans réflexion, qu'aucun débogage ou l'introspection ne sera jamais en principe être tenue.

En Python 3, la « définition imbriquée » ne fonctionne tout simplement pas de travail - il, une métaclasse doit être passé comme un argument mot-clé à la classe, comme dans class A(metaclass=Mcl):, définissant ainsi __metaclass__ dans le corps n'a pas d'effet. Je crois que cela suggère également qu'une définition de métaclasse imbriqué dans le code Python 2 est-il approprié probablement que si vous êtes sûr que le code ne sera jamais besoin d'être porté à Python 3 (puisque vous faites ce port beaucoup plus difficile, et vous devrez de nid à la définition de métaclasse dans le but) - « jetable » code, autrement dit, qui ne sera pas là dans quelques années quand une version de Python 3 acquiert d'énormes avantages incontestables de la vitesse, la fonctionnalité ou troisième le soutien du parti, Over Python 2.7 (la dernière version de Python jamais 2).

Le code qui vous attendez pour être jetable, comme l'histoire de l'informatique nous montre, a une habitude attachante de vous surprendre tout à fait, et étant encore environ 20 ans plus tard (alors que peut-être le code que vous avez écrit autour en même temps « pour les âges » est tout à fait oublié ;-). Cela semble certainement éviter la définition imbriquée proposer des métaclasses.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top