2017-05-26 3 views
0

Ich versuche, meinen Kopf um Metaklasse zu bekommen, aber ich kann immer noch nicht wirklich das Konzept davon bekommen.Understanding Metaklassen in Python

Soviel ich weiß:

Jede Klasse ist selbst eine Instanz vom Typ „Typ“ - also „Berufung“ eine Klasse ruft einfach die Methode __call__ auf seiner Klasse - die Art des __call__ sein geschieht. Die Wirkung von type.__call__ ist genau das: auf Code wie:

Klasse A: Pass b = A()

Die Reihenfolge der Schritte, ich weiß hier:

1. type.__call__ die Klasse A empfängt selbst als sein erster Parameter.

  1. Es ruft die A.__new__ - im Pseudocode könnten wir schreiben instance = A.__new__(cls) als was läuft.

3.That gibt eine Instanz der Klasse "A"

4.Then es nennt __init__ auf der Instanz (instance.__init__()) ... und gibt diese Instanz Instanz

zurückkehren Aber jetzt Sehen Sie den Code unten:

class MetaOne(type): 
    def __new__(meta, classname, supers, classdict): 
     print('In MetaOne.new:', meta, classname, supers, classdict, sep='\n...') 
     return type.__new__(meta, classname, supers, classdict) 

class Eggs: 
    pass 

print('making class') 

class Spam(Eggs, metaclass=MetaOne): 
    data = 1 
    def meth(self, arg): 
     return self.data + arg 

print('making instance') 
X = Spam() 
print('data:', X.data, X.meth(2)) 

die Ausgabe von diesem Skript wird wie folgt:

So
making class 
In MetaOne.new: 
...<class '__main__.MetaOne'> 
...Spam 
...(<class '__main__.Eggs'>,) 
...{'__qualname__': 'Spam', '__module__': '__main__', 'meth': <function Spam.met 
h at 0x00000000010C1D08>, 'data': 1} 
making instance 
data: 1 3 

nach meinem Verständnis ist dies die Folge von Schritten:

  1. Da Spam ist eine Instanz MetaOne, X = Spam() Aufruf würde versuchen, die __call__ Methode der MetaOne Klasse zu nennen, die nicht da ist.

  2. Da MetaOne vom Typ erbt, würde es die __call__ Methode des Typs class mit Spam als das erste Argument aufrufen.

Danach wird der Anruf landet in der __new__ Methode der MetaOne Klasse, aber es sollte Spam als erster param enthalten.

Woher kommt meta Argument der MetaOne-Klasse ins Bild kommen.

Bitte helfen Sie mir in meinem Verständnis.

Antwort

0

Da Spam ist eine Instanz MetaOne, ruft X = Spam() würde die __call__ Methode der MetaOne Klasse Anruf versuchen, die nicht da ist.

Das ist die soruce Ihrer Verwirrung - die __call__ (oder __new__ und __init__) des Metaklasse ist nicht aufgerufen, wenn Sie normale Instanzen der Klasse erstellen.

Auch, weil es keine __call__ Methode für MetaOne ist, die üblichen Vererbungsregeln gelten: die __call__ Methode auf MetaOne der übergeordneten Klasse verwendet wird (und es ist type.__call__)

Die metaclass' __new__ und __init__ Methoden aufgerufen werden, wenn die Der Klassenkörper selbst wird ausgeführt (wie Sie in Ihrem Beispiel sehen können, erscheint "print" in der Metaklasse "__new__" vor dem "making instance" -Text).

Wenn eine Instanz von Span Schaffung selbst werden die Metaklasse Methoden __new__ und __init__ nicht genannt - die Metaklasse __call__ heißt - und es ist das, dass die (Span) und __new____init__ Klasse ausführt. Mit anderen Worten: Die Metaklasse __call__ ist verantwortlich für den Aufruf der __new__ und __init__ der "normalen" Klasse.

Da MetaOne vom Typ erbt würde es die Aufruf Methode der Typklasse mit Spam als erstes Argument nennen.

Und es tut, aber Sie haben keine Druckanweisungen auf „Ansicht“, dies geschieht:

class MyMeta(type): 
    def __new__(metacls, name, bases, namespace): 
     print("At meta __new__") 
     return super().__new__(metacls, name, bases, namespace) 
    def __call__(cls, *args, **kwd): 
     print ("at meta __call__") 
     return super().__call__(*args, **kwd) 

def Egg(metaclass=MyMeta): 
    def __new__(cls): 
     print("at class __new__") 

Wenn ich dies an der ineractive Konsole einfügen, an dieser Stelle druckt:

At meta __new__ 

Dann auf der interaktiven Sitzung gehen:

In [4]: fried = Egg() 
at meta __call__ 
at class __new__ 

Und das zusätzliche Gedanken-verdrehende Ding ist das: "Typ ist Typ eigene Metaklasse": was bedeutet, dass type 's __call__ ist auch verantwortlich für die __new__ und __init__ Methoden auf der Metaklasse selbst, wenn eine neue (nicht-Meta) -Klasse Körper wird ausgeführt.