2016-07-28 12 views
5

Um Ihre Augen zu fangen:Kann Metaklasse aufrufbar sein?

Ich denke, die Dokumentation könnte falsch sein!

Gemäß Python 2.7.12 Dokumentation, 3.4.3. Customizing Klasse creation¶:

__metaclass__ Diese Variable kann jede aufrufbare akzeptieren Argumente für Namen, Basen und dict sein. Bei der Klassenerstellung wird anstelle des eingebauten das aufrufbare type() verwendet.

Neu in Version 2.2. Jedoch

, this article argumentiert:

Q: Wow! Kann ich ein beliebiges Objekt als __metaclass__ verwenden?

A: Nein. Es muss eine Unterklasse vom Typ des Basisobjekts sein. ...

Also habe ich ein Experiment auf meinem eigenen:

class metacls(list): # <--- subclassing list, rather than type 
    def __new__(mcs, name, bases, dict): 
     dict['foo'] = 'metacls was here' 
     return type.__new__(mcs, name, bases, dict) 

class cls(object): 
    __metaclass__ = metacls 
    pass 

Das gibt mir:

Traceback (most recent call last): 
    File "test.py", line 6, in <module> 
    class cls(object): 
    File "test.py", line 4, in __new__ 
    return type.__new__(mcs, name, bases, dict) 
TypeError: Error when calling the metaclass bases 
    type.__new__(metacls): metacls is not a subtype of type 

So ist das Dokument wirklich falsch?

Antwort

5

Nein, jede aufrufbar wird tun. In Ihrem Fall hat die type.__new__() Methode eine Einschränkung, die Sie verletzen; Das hat nichts damit zu tun, was Sie __metaclass__ zuweisen.

Eine Funktion ist eine aufrufbare:

def metaclass_function(name, bases, body): 
    return type(name, bases, body) 

Dieser gibt nur das Ergebnis der type() (nicht type.__new__()), aber es ist nur aufrufbar. Der Rückgabewert wird als Klasse verwendet. Man könnte wirklich etwas zurückgeben:

>>> class Foo(object): 
...  __metaclass__ = lambda *args: [] 
... 
>>> Foo 
[] 

Hier ist die abrufbaren eine Liste Instanz erzeugt, so Foo an eine Liste gebunden ist. Nicht sehr nützlich, aber __metaclass__ wird gerade aufgerufen, etwas zu produzieren, und dass etwas direkt verwendet wird.

In Ihrem Beispiel das erste Argument type.__new__() ist kein Typ, und es ist die aufrufen, die fehlschlägt. mcs ist an list gebunden und keine (Unterklasse von) type. type.__new__() kann solche Einschränkungen frei festlegen.

Jetzt, da eine Metaklasse immer noch an das Klassenobjekt gebunden ist (type(ClassObj) gibt es zurück) und wird verwendet, wenn Attribut-Look-Ups für das Klassenobjekt (wo das Attribut in der Klasse MRO nicht verfügbar ist) aufgelöst werden in der Regel ein hupen gute Idee, um es zu einer Unterklasse von type machen, denn das gibt Ihnen die richtige Umsetzung von Dingen wie __getattribute__. Aus diesem Grund machen type.__new__() eine Einschränkung dessen, was als erstes Argument übergeben werden kann; Es ist das erste Argument, das type() an das zurückgegebene Klassenobjekt anfügt.

+0

"gebunden an" bedeutet "eine Unterklasse von", oder? –

+0

@sunqingyao: Nein, "gebunden" bedeutet zugeordnet. Die Zuweisung 'Foo = []' wird ausgeführt. Eine 'class'-Anweisung wird grundsätzlich als Funktion behandelt; Der Body wird wie in einer Funktion ausgeführt und alle lokalen Namen werden in ein Dictionary extrahiert. Dann wird das Klassenobjekt erzeugt, indem man 'bodydict.get ('__ metaclass__', type)' (also entweder den Wert von '__metaclass__' oder, falls fehlt,' type') aufruft und den Klassennamen, die Klassenbasen und das Körperwörterbuch. * Was zurückgegeben wird * wird dem Namen der Klasse zugewiesen. Binding ist der Fachausdruck; andere Dinge binden auch, wie 'import' oder' for'. –

+0

Aber mit der Aussage "' mcs 'ist an' liste' gebunden ", scheinst du zu meinen,' 'mcs' ist eine Instanz einer Unterklasse von' list' ". –