Überlegen Sie:
%Vor% Das Obenstehende stammt vom Udacity Python-Kurs. Ich habe festgestellt, dass ich show_info
für Beispiel billy_cyrus
mit einem der folgenden Verfahren aufrufen kann:
Ich bin neugierig, warum. Gibt es einen Unterschied zwischen den beiden Methoden? Wenn ja, wann würde man gegen die andere verwendet werden? Ich verwende Python 3.6, wenn das wichtig ist.
In Bezug auf den Aufruf der Methode gibt es meistens keinen Unterschied. In Bezug darauf, wie die zugrunde liegende Maschinerie funktioniert, gibt es einen Unterschied.
Da show_info
eine Methode ist, ist es ein descriptor in der Klasse. Das bedeutet, wenn Sie über eine Instanz darauf zugreifen, in der sie nicht von einem anderen Schatten gespiegelt wird Attribut , der Operator .
ruft __get__
für den Deskriptor, um eine gebundene Methode für diese Instanz zu erstellen. Eine gebundene Methode ist im Grunde eine Closure, die den Parameter self
für Sie vor allen anderen von Ihnen übergebenen Argumenten übergibt. Sie können die Bindung wie folgt sehen:
Jedes Mal, wenn Sie den Operator .
für eine Klassenmethode verwenden, wird ein anderer Abschluss erstellt.
Wenn Sie dagegen über das Klassenobjekt auf die Methode zugreifen, wird sie nicht gebunden. Die Methode ist ein Deskriptor, der nur ein reguläres Attribut der Klasse ist:
%Vor% Sie können das genaue Verhalten des Bindens einer Methode vor dem Aufruf simulieren, indem Sie ihr __get__
self: aufrufen:
Auch dies wird in 99,99% der Fälle für Sie keinen Unterschied machen, da die Funktionen bound_meth()
und Parent.bound_meth(billy_cyrus)
letztendlich dasselbe zugrundeliegende Funktionsobjekt mit denselben Parametern aufrufen.
Wo es darauf ankommt
Es gibt ein paar Orte, an denen es wichtig ist, wie Sie eine Klassenmethode aufrufen. Ein häufiger Anwendungsfall ist, wenn Sie eine Methode überschreiben, aber die in der übergeordneten Klasse angegebene Definition verwenden möchten. Angenommen, ich habe eine Klasse, die ich "unveränderbar" gemacht habe, indem ich __setattr__
. Ich kann immer noch Attribute für die Instanz festlegen, wie in der folgenden Methode __init__
:
Wenn ich versucht habe, einen normalen Aufruf von __setattr__
in __init__
zu machen, indem ich self.a = a
mache, würde ein ValueError
jedes Mal ausgelöst. Aber mit object.__setattr__
kann ich diese Einschränkung umgehen. Alternativ könnte ich super().__setattr__('a', a)
für den gleichen Effekt oder self.__dict__['a'] = a
für einen sehr ähnlichen Effekt verwenden.
@Silvio Mayolos Antwort hat ein anderes gutes Beispiel, in dem Sie die Klassenmethode absichtlich als eine Funktion verwenden möchten, die auf viele Objekte angewendet werden kann.
Ein anderer wichtiger Punkt (wenn auch nicht in Bezug auf Aufrufmethoden) ist, wenn Sie andere gebräuchliche Deskriptoren wie property
. Im Gegensatz zu Methoden sind Eigenschaften Datendeskriptoren . Dies bedeutet, dass sie eine __set__
Methode (und optional __delete__
) zusätzlich zu __get__
definieren. Eine Eigenschaft erstellt ein virtuelles Attribut, dessen Getter und Setter beliebig komplexe Funktionen sind und nicht nur einfache Zuweisungen. Um eine Eigenschaft richtig zu verwenden, müssen Sie dies über die Instanz tun. Zum Beispiel:
Jetzt können Sie etwas wie
tun %Vor%Wenn Sie versuchen, über die Klasse auf die Eigenschaft zuzugreifen, können Sie das zugrunde liegende Deskriptorobjekt abrufen, da es ein ungebundenes Attribut ist:
%Vor% Nebenbei bemerkt, das Ausblenden von Attributen mit demselben Namen wie eine Eigenschaft in __dict__
ist ein netter Trick, der funktioniert, weil Datendeskriptoren in einer Klasse __dict__
Einträge in der Instanz __dict__
trumpfen, obwohl Instanz __dict__
Einträge übertrumpfen Nicht-Daten-Deskriptoren in einer Klasse.
Wo es seltsam werden kann
Sie können eine Klassenmethode mit einer Instanzmethode in Python überschreiben. Das würde bedeuten, dass type(foo).bar(foo)
und foo.bar()
überhaupt nicht dieselbe zugrunde liegende Funktion aufrufen. Dies ist für magische Methoden irrelevant, da sie immer den vorherigen Aufruf verwenden, aber es kann einen großen Unterschied für normale Methodenaufrufe machen.
Es gibt mehrere Möglichkeiten, eine Methode für eine Instanz zu überschreiben . Dasjenige, das ich am intuitivsten finde, ist, das Instanzattribut auf eine gebundene Methode zu setzen. Hier ist ein Beispiel für eine modifizierte billy_cyrus
unter der Annahme der Definition von Parent
in der ursprünglichen Frage:
In diesem Fall hätte der Aufruf der Methode für die Instanz gegenüber der Klasse vollständig unterschiedliche Ergebnisse. Dies funktioniert nur, weil Methoden übrigens Nicht-Daten Deskriptoren sind. Wenn sie Datendeskriptoren wären (mit einer __set__
-Methode), würde die Zuweisung billy_cyrus.show_info = alt_show_info.__get__(billy_cyrus, Parent)
nichts überschreiben, sondern stattdessen nur zu __set__
umleiten und sie manuell in b setzen
billy_cyrus
's __dict__
würde es einfach ignorieren, wie es bei einer Eigenschaft passiert.
Zusätzliche Ressourcen
Hier sind ein paar Ressourcen zu Deskriptoren:
Es gibt keinen semantischen Unterschied zwischen den beiden. Es ist nur eine Frage des Stils. Normalerweise würden Sie billy_cyrus.show_info()
bei normaler Verwendung verwenden, aber die Tatsache, dass der zweite Ansatz zulässig ist, erlaubt Ihnen, Parent.show_info
zu verwenden, um die Methode selbst als ein erstklassiges Objekt zu erhalten. Wenn das nicht erlaubt wäre, dann wäre es nicht möglich (oder zumindest ziemlich schwierig), so etwas zu tun.
Tags und Links python