Was ist der Unterschied zwischen Typ und Typ .__ new__ in Python?
-
25-09-2019 - |
Frage
Ich war ein Metaklasse zu schreiben und habe es versehentlich, wie folgt aus:
class MetaCls(type):
def __new__(cls, name, bases, dict):
return type(name, bases, dict)
... statt wie folgt:
class MetaCls(type):
def __new__(cls, name, bases, dict):
return type.__new__(cls, name, bases, dict)
Was genau ist der Unterschied zwischen diesen beiden metaclasses? Und genauer gesagt, was die Ursache der erste, der nicht richtig funktionieren (einige Klassen nicht in der Metaklasse genannt wurden)?
Lösung
Im ersten Beispiel sind Sie eine ganz neue Klasse zu erstellen:
>>> class MetaA(type):
... def __new__(cls, name, bases, dct):
... print 'MetaA.__new__'
... return type(name, bases, dct)
... def __init__(cls, name, bases, dct):
... print 'MetaA.__init__'
...
>>> class A(object):
... __metaclass__ = MetaA
...
MetaA.__new__
>>>
, während im zweiten Fall die Sie anrufen Eltern __new__
:
>>> class MetaA(type):
... def __new__(cls, name, bases, dct):
... print 'MetaA.__new__'
... return type.__new__(cls, name, bases, dct)
... def __init__(cls, name, bases, dct):
... print 'MetaA.__init__'
...
>>> class A(object):
... __metaclass__ = MetaA
...
MetaA.__new__
MetaA.__init__
>>>
Andere Tipps
Das erste, was Sie brauchen, um herauszufinden, ist, wie object.__new__()
funktioniert.
Hier ist es von der Dokumentation :
object.__new__(cls[, ...])
aufgerufen, um eine neue Instanz der Klasse
cls
zu erstellen.__new__()
ist eine statische Verfahren (spezielle Gefasste, so dass Sie es nicht als solche deklarieren muss), der nimmt die Klasse, von denen eine Instanz als erstes Argument angefordert wurde. Die übrigen Argumente sind diejenigen, auf das Objekt Konstruktor übergeben Ausdruck (der Aufruf der Klasse). Der Rückgabewert von__new__()
sollte die neue Objektinstanz (in der Regel eine Instanz voncls
).Typische Implementierungen erstellen Sie eine neue Instanz der Klasse durch den Aufruf die
__new__()
Methodesuper(currentclass, cls).__new__(cls[, ...])
der Superklasse mit entsprechenden Argumenten und dann Modifizieren die neu erstellte Instanz als notwendig, bevor es zurück.Wenn
__new__()
eine Instanz voncls
zurückgibt, die__init__()
Methode der neue Instanz wird wie__init__(self[, ...])
aufgerufen werden, woself
ist die neue Instanz und die übrigen Argumente sind die gleichen wie wurden__new__()
übergeben.Wenn
__new__()
keine Instanzcls
zurückkehrt, dann ist die__init__()
Methode der neuen Instanz wird nicht aufgerufen werden.
__new__()
soll hauptsächlich Subklassen von erlauben unveränderlich Typen (wieint
,str
odertuple
) Instanzerstellung anpassen. Es ist auch häufig in benutzerdefinierter metaclasses um überschrieben Klasse anpassen Schöpfung.
So in mg. Antwort , wird die ehemalige nicht Funktion __init__
nennen, während die letztere Anrufe funktionieren __init__
nach Aufruf __new__
.
return type(name, bases, dict)
Was Sie wieder aus ist dies eine neue type
, und keine MetaCls
Instanz überhaupt. Folglich definiert Ihre Methoden in MetaCls
(einschließlich __init__
) kann nicht immer genannt werden.
type.__new__
wird im Rahmen genannt werden, dass neue Art zu schaffen, ja, aber der Wert von cls
in diese Funktion gehen wird type
und nicht MetaCls
sein.
Wir verweisen auf die Anmerkung unten, hoffen, dass diese Informationen hilfreich.
class MetaCls(type):
def __new__(cls, name, bases, dict):
# return a new type named "name",this type has nothing
# to do with MetaCls,and MetaCl.__init__ won't be invoked
return type(name, bases, dict)
class MetaCls(type):
def __new__(cls, name, bases, dict):
# return a new type named "name",the returned type
# is an instance of cls,and cls here is "MetaCls", so
# the next step can invoke MetaCls.__init__
return type.__new__(cls, name, bases, dict)
class MyMeta(type):
def __new__(meta, cls, bases, attributes):
print 'MyMeta.__new__'
return type.__new__(meta, cls, bases, attributes)
def __init__(clsobj, cls, bases, attributes):
print 'MyMeta.__init__'
class MyClass(object):
__metaclass__ = MyMeta
foo = 'bar'
Eine andere Möglichkeit, um das gleiche Ergebnis zu erreichen:
cls = "MyClass"
bases = ()
attributes = {'foo': 'bar'}
MyClass = MyMeta(cls, bases, attributes)
MyMeta
ist eine aufrufbare so Python wird die spezielle Methode __call__
verwenden.
Python wird für __call__
aussieht in dem Typ des MyMeta
(die type
in unserem Fall)
"Für neuen Stil Klassen, implizite Anrufungen spezieller Methoden sind nur Arbeit garantiert richtig definiert, wenn sie auf den Typ eines Objekts, nicht in der Instanz Wörterbuch des Objekts "
MyClass = MyMeta(...)
wird interpretiert als:
my_meta_type = type(MyMeta)
MyClass = my_meta_type.__call__(MyMeta, cls, bases, attributes)
Im Inneren des type.__call__()
ich so etwas wie dies vorstellen:
MyClass = MyMeta.__new__(MyMeta, cls, bases, attributes)
meta_class = MyClass.__metaclass__
meta_class.__init__(MyClass, cls, bases, attributes)
return MyClass
MyMeta.__new__()
wird entscheiden, wie der MyClass
gebaut wird:
type.__new__(meta, cls, bases, attributes)
die korrekte metaclass gesetzt (die MyMeta
ist) für MyClass
type(cls, bases, attributes)
wird das Standard metaclass (welcher Typ ist) für MyClass
Es ist alles ziemlich gut beschrieben hier .
Wenn Sie die richtige Art von Objekt nicht zurück, es hat keinen Sinn zu definieren, eine benutzerdefinierte metaclass.