Frage

Heute habe ich über eine überraschende Definition einer Metaklasse in Python kommen hier , mit die Metaklasse Definition effektiv inlined. Der relevante Teil ist

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

Wann ist es sinnvoll, eine solche Inline-Definition zu benutzen?

Weitere Argumente:

Ein Argument einer Möglichkeit wäre, dass die erzeugte metaclass nicht wiederverwendbar ist an anderer Stelle mit dieser Technik. Ein Zähler Argument ist, dass ein gemeinsames Muster in Verwendung Metaklassen eine Metaklasse zu definieren und es in einer Klasse unter Verwendung von und dann von diesem inhertiting. Zum Beispiel in einem konservativen metaclass die Definition

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

geschrieben wurden, könnten als

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

Alle anderen Überlegungen?

War es hilfreich?

Lösung

Wie jede andere Form von verschachtelten Klassendefinition, eine verschachtelte metaclass mehr sein kann „kompakt und bequem“ (solange du bist OK mit nicht, dass metaclass außer durch Vererbung Wiederverwendung) für viele Arten von „produktiven Einsatz“, aber wird etwas unbequem für die Fehlersuche und Selbstbeobachtung kann.

Grundsätzlich statt gibt das metaclass einen richtigen, Top-Level-Namen, Sie gehen mit all benutzerdefinierten metaclasses in einem Modul definiert, um am Ende undistiguishable sind voneinander auf der Grundlage ihrer __module__ und __name__ Attribute (das, was Python ist Verwendungen ihre repr zu bilden, wenn erforderlich). Bedenken Sie:

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

IOW, wenn Sie untersuchen möchten „welcher Typ Klasse A“ (ein Metaklasse ist die Art der Klasse, erinnern), erhalten Sie eine klare und sachdienliche Antwort - es ist Mcl im Hauptmodul. Allerdings, wenn Sie möchten, untersuchen „welchen Typ der Klasse B ist“, ist die Antwort nicht so nützlich: es sagt es ist __metaclass__ im main Modul, aber das ist nicht einmal wahr:

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

... es nicht so etwas, eigentlich, dass repr ist irreführend und nicht sehr hilfreich, -).

Eine repr Klasse ist im Wesentlichen '%s.%s' % (c.__module__, c.__name__) - ein einfache, nützliche und konsistente Regel - aber in vielen Fällen, wie die class Anweisung in Modul Umfang nicht eindeutig zuzuordnen ist, oder gar nicht am Modulumfang ist (sondern innerhalb eine Funktion oder Klasse Körper) oder gar nicht vorhandenes (Klassen ohne class Anweisung gebaut werden können natürlich durch ihre metaclass expliziten Aufruf), kann dies etwas irreführend sein (und die beste Lösung zu vermeiden, so weit wie möglich, jene eigentümlichen Fälle, es sei denn, wesentlicher Vorteil kann mit ihnen erzielt werden). Betrachten wir zum Beispiel:

>>> 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

mit zwei class Anweisung zum gleichen Umfang, erneut bindet die zweiten die Namen (hier A), aber vorhandene Instanzen beziehen sich auf den ersten von Objekt des Namen zu binden, nicht mit Namen - so beiden Klassenobjekte bleiben, ein zugänglich nur durch die type (oder __class__ Attribut) seiner Instanzen (wenn überhaupt - wenn keines, das erste Objekt verschwindet) - die beiden Klassen den gleichen Namen und Modul (und damit die gleiche Darstellung) haben, aber sie sind verschieden Objekte. Klassen innerhalb Klasse oder einen Funktionskörper verschachtelt oder erstellt von direkt die Metaklasse (einschließlich type) aufrufen, ähnliche Verwirrung führen kann, wenn das Debuggen oder Selbstbeobachtung ist immer gefragt.

Also, die Metaklasse Verschachtelung ist OK, wenn Sie nie zu debuggen benötigen oder anderweitig introspect diesen Code und können mit gelebt werden, wenn wer auch immer so diesen Macken tun verstehen (obwohl es nie als mit einem schön wie praktisch sein, Name, natürlich - wie eine Funktion mit lambda codiert Debuggen kann unmöglich noch so bequem sein, wie man mit def codiert Debugging). Analog zu lambda vs def kann man vernünftigerweise behaupten, dass anonyme „verschachtelte“ Definition OK für metaclasses ist, die sind so ganz einfach, solche nicht-brainers, dass kein Debugging oder Selbstbeobachtung jemals denkbar erforderlich.

In Python 3, die „verschachtelte Definition“ funktioniert einfach nicht - da, ein metaclass muss auf die Klasse als Schlüsselwort-Argument übergeben werden, wie in class A(metaclass=Mcl):, __metaclass__ im Körper so definiert, hat keine Wirkung. Ich glaube, das zeigt auch, dass eine verschachtelte metaclass Definition in Python 2-Code wahrscheinlich nur sinnvoll, wenn Sie sicher wissen, dass Code niemals 3 bis Python portiert werden müssen (da Sie diesen Port so viel schwieriger machen, und müssen de-Nest der metaclass Definition für die Zwecke) - „Wegwerf“ Code, mit anderen Worten, die in ein paar Jahren, wenn eine Version von Python 3 akquiriert große, überzeugende Vorteile von Geschwindigkeit, Funktionalität werden nicht um, oder dritt- Party-Unterstützung, over Python 2.7 (die letzte jemals Version von Python 2).

Code, den Sie erwarten Wegwerf zu sein, wie die Geschichte der Berechnung zeigt uns, hat eine liebenswerte Gewohnheit, die Sie völlig überraschen, und später noch rund 20 Jahre (während vielleicht der Code, den Sie um schrieb die gleiche Zeit „für die Ewigkeit“ ist ganz und gar vergessen ;-). Dies würde sicherlich scheint verschachtelte Definition von metaclasses Vermeidung vorzuschlagen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top