Warum kann '__subclasshook__' auf die Metaklasse monkeypatched werden, aber '__instancecheck__' nicht?

8

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.

%Vor%

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

    
ely 11.12.2013, 20:04
quelle

1 Antwort

2

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:

%Vor%

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

    
Simon Sagi 16.12.2013, 17:36
quelle

Tags und Links