Warum akzeptiert das Metaklassen-Schlüsselwort-Argument der Klassendefinition eine aufrufbare Funktion?

8

Hintergrund

Die Python 3 Dokumentation beschreibt deutlich, wie die Metaklasse von a Klasse ist festgelegt:

  
  • Wenn keine Basen und keine explizite Metaklasse angegeben sind, wird type () verwendet
  •   
  • Wenn eine explizite Metaklasse angegeben wird und es sich nicht um eine Instanz von type () handelt, wird sie direkt als Metaklasse
  • verwendet   
  • Wenn eine Instanz von type () als explizite Metaklasse angegeben wird oder Basen definiert sind, wird die am weitesten abgeleitete Metaklasse
  • verwendet   

Daher ist es gemäß der zweiten Regel möglich, eine Metaklasse anzugeben, die eine aufrufbare Funktion verwendet. Z. B.

%Vor%

Frage 1

Ist die Metaklasse von MyClass : metaclass_callable oder MyMetaclass ? Die zweite Regel in der Dokumentation besagt, dass das bereitgestellte Callable "direkt als Metaklasse verwendet wird". Es scheint jedoch sinnvoller zu sein, dass die Metaklasse MyMetaclass seit

ist
  • MyClass und MyDerived haben den Typ MyMetaclass ,
  • metaclass_callable wird einmal aufgerufen und scheint dann nicht wiederherstellbar zu sein,
  • abgeleitete Klassen verwenden (so weit ich das beurteilen kann) metaclass_callable in keiner Weise (sie verwenden MyMetaclass ).

Frage 2

Gibt es irgendetwas, was Sie mit einer Callable tun können, die Sie mit einer Instanz von type nicht tun können? Was ist der Zweck, eine willkürliche Callable zu akzeptieren?

    
Neil G 13.10.2016, 19:54
quelle

3 Antworten

7

In Bezug auf Ihre erste Frage sollte die Metaklasse MyMetaclass sein (was es ist):

%Vor%

Der Grund ist, dass, wenn die Metaklasse keine Instanz des Typs python ist, die Methaklasse aufgerufen wird, indem diese Argumente an name, bases, ns, **kwds übergeben werden (siehe new_class ) und da Sie Ihre echte Metaklasse in dieser Funktion zurückgeben, wird sie korrekt Geben Sie für Metaklasse ein.

Und über die zweite Frage:

  

Was ist der Zweck der Annahme einer willkürlichen aufrufbar?

Es gibt keinen speziellen Zweck, ist eigentlich die Art von Metaklassen , denn wenn eine Instanz einer Klasse aufgerufen wird, ruft sie immer die Metaklasse auf, indem sie ihre __call__ -Methode aufruft:

%Vor%

Das bedeutet, dass Sie alle aufrufbaren Nachrichten als Ihre Metaklasse weitergeben können. Wenn Sie es zum Beispiel mit einer verschachtelten Funktion testen, ist das Ergebnis immer noch dasselbe:

%Vor%

Für weitere Informationen hier ist, wie Python eine Klasse kiste:

Er ruft die Funktion new_class auf, die prepare_class in sich selbst aufruft, und dann, wie Sie im prepare_class python sehen können, ruft sie die Methode __prepare__ der entsprechenden Metaklasse auf. co_de% function) und den entsprechenden Namespace für die Klasse erstellen.

Also hier ist die Hierarchie der Ausführung der Metakalss Methoden:

  1. _calculate_meta 1
  2. __prepare__
  3. __call__
  4. __new__

Und hier ist der Quellcode:

%Vor%

1. Beachten Sie, dass es implizit innerhalb der new_class -Funktion und vor der Rückgabe aufgerufen wird.

    
Kasramvd 13.10.2016 20:14
quelle
2

Nun, die type ist natürlich MyMetaClass . metaclass_callable wird anfänglich als Metaklasse ausgewählt, da in metaclass angegeben wurde. kwarg und als solches wird __call__ (ein einfacher Funktionsaufruf) ausgeführt.

Es wird einfach so aufgerufen, dass% ce_de% aufgerufen wird und dann print aufgerufen wird (was MyMetaClass.__call__ aufruft, da type.__call__ nicht für __call__ überschrieben wurde). Dort wird die Zuweisung von MyMetaClass vorgenommen zu cls.__class__ .

  

MyMetaClass wird einmal aufgerufen und scheint dann nicht wiederherstellbar zu sein

Ja, es wird nur anfänglich aufgerufen und steuert dann die Kontrolle über metaclass_callable . Ich kenne keine Klassenattribute, die diese Informationen enthalten.

  

abgeleitete Klassen verwenden (so weit ich das beurteilen kann) MyMetaClass in keiner Weise.

Nein, wenn kein metaclass_callable explizit definiert ist, die beste Übereinstimmung für die Metaklassen von metaclass (hier bases ) wird verwendet (resultierend in MyClass ).

Wie bei der Frage MyMetaClass ist es ziemlich sicher, dass alles, was Sie mit einer aufrufbaren Funktion tun können, auch möglich ist, indem Sie eine Instanz von type mit 2 entsprechend überschreiben. Was why betrifft, möchten Sie vielleicht nicht die vollständige Klassenerstellung durchführen, wenn Sie nur geringfügige Änderungen vornehmen möchten, wenn Sie tatsächlich eine Klasse erstellen.

    
Jim Fasarakis Hilliard 13.10.2016 20:10
quelle
2

Was Frage 1 angeht, denke ich, dass die "Metaklasse" einer Klasse cls als type(cls) verstanden werden sollte. Diese Art zu verstehen ist kompatibel mit Pythons Fehlermeldung im folgenden Beispiel:

%Vor%

Das heißt, entsprechend der Fehlermeldung ist die Metaklasse einer Klasse eine Klasse, obwohl die zur Konstruktion der Klasse verwendete Callable einfach alles sein kann.

In Bezug auf die zweite Frage, in der Tat mit einer Unterklasse des Typs, der als Metaklasse verwendet wird, können Sie das gleiche wie bei jeder anderen aufrufbaren Funktion tun. Insbesondere ist es möglich, dass es etwas ergibt, das nicht seine Instanz ist:

%Vor%

Warum Python die Freiheit bietet, irgendwelche Calls zu benutzen: Das vorherige Beispiel zeigt, dass es eigentlich egal ist, ob das Callable ein Typ ist oder nicht.

Übrigens, hier ist ein lustiges Beispiel: Es ist möglich, Metaklassen zu programmieren, die selbst eine Metaklasse haben, die sich von type unterscheidet - nennen wir es eine Metamethaklasse. Die metametaclass implementiert, was passiert, wenn eine Metaklasse aufgerufen wird. Auf diese Weise ist es möglich, eine Klasse mit zwei Basen zu erstellen, deren Metaklassen nicht Unterklassen voneinander sind (vergleiche mit Pythons Fehlermeldung im obigen Beispiel!). Tatsächlich ist nur die Metaklasse der resultierenden Klasse eine Unterklasse der Metaklasse der Basen, und diese Metaklasse wird im Handumdrehen erzeugt:

%Vor%

Was weniger Spaß macht: Das vorhergehende Beispiel wird in Python 3 nicht funktionieren. Wenn ich richtig verstehe, erstellt Python 2 die Klasse und überprüft, ob ihre Metaklasse eine Unterklasse aller ihrer Basen ist, während Python 3 zuerst prüft, ob es eine Basis gibt, deren Metaklasse eine Oberklasse der Metaklassen aller anderen Basen ist, und nur dann die neue Klasse erstellt. Das ist aus meiner Sicht ein Rückschritt. Aber das soll das Thema einer neuen Frage sein, die ich posten werde ...

Bearbeiten : Die neue Frage lautet hier

    
Simon King 13.10.2016 21:15
quelle

Tags und Links