Genaue Erklärung von Armin Ronacher oben, wobei er seine Antworten erweitert, damit Anfänger wie ich es gut verstehen:
Der Unterschied in den in einer Klasse definierten Methoden, ob statische oder Instanzmethode (es gibt noch einen anderen Typ - Klassenmethode - hier nicht besprochen, also überspringen), lag in der Tatsache, ob sie irgendwie an die Klasseninstanz gebunden sind oder nicht. Angenommen, die Methode erhält zur Laufzeit einen Verweis auf die Klasseninstanz
class C:
a = []
def foo(self):
pass
C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C()
c # this is the class instance
Die __dict__Dictionary-Eigenschaft des Klassenobjekts enthält den Verweis auf alle Eigenschaften und Methoden eines Klassenobjekts und damit
>>> C.__dict__['foo']
<function foo at 0x17d05b0>
Die Methode foo ist wie oben beschrieben zugänglich. Ein wichtiger Punkt, der hier zu beachten ist, ist, dass alles in Python ein Objekt ist und daher Verweise im obigen Wörterbuch selbst auf andere Objekte verweisen. Lassen Sie mich sie Class Property Objects nennen - oder als CPO im Rahmen meiner Antwort der Kürze halber.
Wenn ein CPO ein Deskriptor ist, ruft der Python-Interpreter die __get__()Methode des CPO auf, um auf den darin enthaltenen Wert zuzugreifen.
Um festzustellen, ob ein CPO ein Deskriptor ist, prüft der Python-Interpretor, ob er das Deskriptorprotokoll implementiert. Das Deskriptorprotokoll zu implementieren bedeutet, 3 Methoden zu implementieren
def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)
für zB
>>> C.__dict__['foo'].__get__(c, C)
wo
self ist der CPO (es kann sich um eine Instanz von list, str, function usw. handeln) und wird von der Laufzeit bereitgestellt
instance ist die Instanz der Klasse, in der dieser CPO definiert ist (das Objekt 'c' oben) und muss von uns explizit angegeben werden
ownerist die Klasse, in der dieser CPO definiert ist (das Klassenobjekt 'C' oben) und von uns bereitgestellt werden muss. Dies liegt jedoch daran, dass wir es auf dem CPO aufrufen. Wenn wir es für die Instanz aufrufen, müssen wir dies nicht angeben, da die Laufzeit die Instanz oder ihre Klasse bereitstellen kann (Polymorphismus).
value ist der beabsichtigte Wert für den CPO und muss von uns geliefert werden
Nicht alle CPO sind Deskriptoren. Beispielsweise
>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510>
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'
Dies liegt daran, dass die Listenklasse das Deskriptorprotokoll nicht implementiert.
Daher ist das Argument self in c.foo(self)erforderlich, da seine Methodensignatur tatsächlich dies ist C.__dict__['foo'].__get__(c, C)(wie oben erläutert, wird C nicht benötigt, da es herausgefunden oder polymorphisiert werden kann). Aus diesem Grund erhalten Sie auch einen TypeError, wenn Sie das erforderliche Instanzargument nicht übergeben.
Wenn Sie feststellen, dass die Methode weiterhin über das Klassenobjekt C referenziert wird und die Bindung mit der Klasseninstanz durch Übergeben eines Kontexts in Form des Instanzobjekts an diese Funktion erreicht wird.
Dies ist ziemlich beeindruckend, denn wenn Sie keinen Kontext oder keine Bindung an die Instanz beibehalten möchten, müssen Sie lediglich eine Klasse schreiben, um den Deskriptor-CPO zu verpacken und seine __get__()Methode zu überschreiben, sodass kein Kontext erforderlich ist. Diese neue Klasse wird als Dekorateur bezeichnet und über das Schlüsselwort angewendet@staticmethod
class C(object):
@staticmethod
def foo():
pass
Das Fehlen eines Kontexts im neu verpackten CPO foolöst keinen Fehler aus und kann wie folgt überprüft werden:
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
Der Anwendungsfall einer statischen Methode ist eher eine Namespace- und Code-Wartbarkeitsmethode (Herausnehmen aus einer Klasse und Bereitstellen im gesamten Modul usw.).
Es ist möglicherweise besser, statische Methoden als Instanzmethoden zu schreiben, wenn dies möglich ist, es sei denn, Sie müssen die Methoden (wie Zugriffsinstanzvariablen, Klassenvariablen usw.) kontexualisieren. Ein Grund ist, die Speicherbereinigung zu vereinfachen, indem keine unerwünschten Verweise auf Objekte beibehalten werden.