Hier ist ein Spielzeugbeispiel, in dem versucht wird, einen Dekorator zu erstellen, der die Deklaration von Attributnamen erlaubt, die Teile der "Schnittstellenprüfung" entlang der Standardmuster __subclasshook__
und __instancecheck__
sein sollten.
Es scheint zu funktionieren, wenn ich die Klasse Foo
dekoriere. Ich mache eine Bar
-Klasse, die nicht mit Foo
zusammenhängt, aber die benötigten Attribute hat und isinstance(instance_of_bar, Foo) == True
korrekt erfüllt.
Aber als weiteres Beispiel mache ich eine Unterklasse von dict
augmented, so dass die key
Werte auch mit getattr
Syntax zugänglich sind (zB ein dict
wobei d['a']
durch% ersetzt werden kann co_de%, um das gleiche Ergebnis zu erhalten). In diesem Fall sind die Attribute nur Instanzattribute, also sollte d.a
funktionieren.
Hier ist der Code. Beachten Sie, dass, da das Beispiel mit der Instanz __instancecheck__
funktioniert, die Option "monkeypatch" für die Bar
-Funktion in die __subclasshook__
-Klasse (die eine Metaklasse hat) gut funktioniert. Es scheint also nicht , dass man die Funktion direkt in der Klassendefinition der Metaklasse definieren muss.
Drucke:
%Vor% Dies ist nur ein Spielzeugbeispiel - ich suche keine besseren Möglichkeiten, meine Foo
-Klasse zu implementieren. Das attrdict
-Beispiel zeigt die monkeyypatched Bar
working. Die anderen beiden Beispiele zeigen das Fehlschlagen von __subclasshook__
für Instanzen, bei denen nur Instanzattribute überprüft werden. In diesen Fällen wird __instancecheck__
nicht einmal aufgerufen.
Ich kann manuell überprüfen, ob die Bedingung aus meiner Funktion __instancecheck__
von der Instanz __instancecheck__
(dh attrdict
ist hasattr(instance_of_attrdict, "x")
nach Bedarf) oder True
erfüllt ist.
Auch hier scheint es für die Instanz von z
zu funktionieren. Dies deutet darauf hin, dass Bar
vom Decorator korrekt angewendet wird und dass das Patchen einer benutzerdefinierten __subclasshook__
nicht das Problem ist. Aber __metaclass__
scheint in diesem Prozess nicht aufgerufen zu werden.
Warum kann __instancecheck__
außerhalb der Klassendefinition der Metaklasse definiert und später hinzugefügt werden, aber nicht __subclasshook__
?
Alles funktioniert wie es sollte. Wenn Sie __metaclass__
verwenden, überschreiben Sie den Klassenerstellungsprozess. Es sieht so aus, als ob dein Monkeypatch für __subclasshook__
funktioniert, aber er wird nur von der Funktion __subclasshook__
des ABCMeta
aufgerufen. Sie können es mit diesem überprüfen:
Um es genauer zu sagen: Der Fall __subclasshook__
funktioniert in diesem Beispiel zufällig, weil sich die __subclasscheck__
der Metaklasse in einigen Situationen auf die __subclasshook__
der Klasse verschiebt. Das __instancecheck__
-Protokoll der Metaklasse steht nicht im Widerspruch zur Definition der Klasse __instancecheck__
, weshalb die monkeypatched Version von __subclasshook__
letztendlich aufgerufen wird, die monkeypatched Version von __instancecheck__
jedoch nicht aufgerufen wird.
Genauer gesagt: Wenn Sie eine Klasse mit der Metaklasse erstellen, ist der Typ der Klasse die Metaklasse. In diesem Fall ABCMeta
. Und die Definition von isinstance()
sagt folgendes: 'isinstance (object, class-or-type-or-tuple) - & gt; bool ', was bedeutet, dass die Instanzprüfung für die angegebene Klasse, den Typ oder das Tupel von Klassen / Typen ausgeführt wird. In diesem Fall wird die Instanzprüfung für ABCMeta
durchgeführt ( ABCMeta.__instancecheck__()
wird aufgerufen). Da der Monkeypatch auf die Klasse Foo
und nicht auf ABCMeta
angewendet wurde, wird die Methode __instancecheck__
von Foo
niemals ausgeführt. Aber die __instancecheck__
der ABCMethod
ruft die __subclasscheck__
-Methode von selbst auf, und diese zweite Methode versucht die Validierung, indem sie die Methode __subclasshook__
der erstellten Klasse ausführt (in diesem Beispiel Foo
).
In diesem Fall können Sie das gewünschte Verhalten erhalten, wenn Sie die Funktionen der Metaklasse wie folgt überschreiben:
%Vor%Und die Ausgabe mit dem aktualisierten Code:
%Vor% Ein anderer Ansatz wäre, Ihre eigene Klasse als Metaklasse zu definieren und die Art des gesuchten __instancecheck__
-Protokolls zu erstellen, so dass sich auf die Definition der Klasse verschiebt of __instancecheck__
, wenn die Definition der Metaklasse einige Fehlerkriterien trifft. Setzen Sie dann __metaclass__
auf diese Klasse innerhalb von Foo
und Ihr vorhandener Decorator sollte so arbeiten wie er ist.
Weitere Informationen: Ein guter Beitrag über Metaklassen