Dokładne wyjaśnienie od Armin Ronacher powyżej, rozszerzając jego odpowiedzi, aby początkujący tacy jak ja dobrze to rozumieli:
Różnica w metodach zdefiniowanych w klasie, zarówno statycznych, jak i instancji (istnieje jeszcze inna metoda typu - klasa - nie omawiana tutaj, więc pomijanie jej) polega na tym, czy są one w jakiś sposób powiązane z instancją klasy, czy nie. Na przykład powiedz, czy metoda otrzymuje odwołanie do instancji klasy podczas działania
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
Właściwość __dict__Dictionary obiektu klasy zawiera odniesienie do wszystkich właściwości i metod obiektu klasy, a tym samym
>>> C.__dict__['foo']
<function foo at 0x17d05b0>
metoda foo jest dostępna jak wyżej. Ważną kwestią, na którą należy tutaj zwrócić uwagę, jest to, że wszystko w Pythonie jest obiektem, a zatem odwołania w powyższym słowniku wskazują same obiekty. Nazwijmy je Obiektami własności klasy - lub CPO w zakresie mojej odpowiedzi za zwięzłość.
Jeśli deskryptor CPO jest deskryptorem, wówczas interpreter Pythona wywołuje __get__()metodę CPO w celu uzyskania dostępu do wartości, którą zawiera.
Aby ustalić, czy CPO jest deskryptorem, interpreter Pythona sprawdza, czy implementuje protokół deskryptora. Aby wdrożyć protokół deskryptora, należy zaimplementować 3 metody
def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)
na przykład
>>> C.__dict__['foo'].__get__(c, C)
gdzie
self jest CPO (może to być instancja listy, str, funkcji itp.) i jest dostarczana przez środowisko wykonawcze
instance jest instancją klasy, w której ten CPO jest zdefiniowany (obiekt „c” powyżej) i musi być dostarczony przez nas jawność
ownerto klasa, w której zdefiniowano ten CPO (obiekt klasy „C” powyżej) i należy go dostarczyć. Jest tak jednak dlatego, że nazywamy to CPO. gdy wywołujemy go w instancji, nie musimy go podawać, ponieważ środowisko wykonawcze może dostarczyć instancję lub jej klasę (polimorfizm)
value jest zamierzoną wartością CPO i musi być przez nas dostarczona
Nie wszystkie CPO są deskryptorami. Na przykład
>>> 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__'
Jest tak, ponieważ klasa listy nie implementuje protokołu deskryptora.
Zatem argument self in c.foo(self)jest wymagany, ponieważ jego podpis metody jest w rzeczywistości taki C.__dict__['foo'].__get__(c, C)(jak wyjaśniono powyżej, C nie jest potrzebny, ponieważ można go znaleźć lub polimorficznie). I dlatego otrzymujesz błąd typu TypeError, jeśli nie przekażesz wymaganego argumentu instancji.
Jeśli zauważysz, że do metody nadal odwołuje się obiekt klasy C, a powiązanie z instancją klasy jest osiągane poprzez przekazanie kontekstu w postaci obiektu instancji do tej funkcji.
Jest to całkiem niesamowite, ponieważ jeśli zdecydujesz się nie utrzymywać kontekstu ani powiązania z instancją, wystarczyło napisać klasę, aby owinąć CPO deskryptora i zastąpić jej __get__()metodę, aby nie wymagała kontekstu. Ta nowa klasa nazywana jest dekoratorem i jest stosowana za pomocą słowa kluczowego@staticmethod
class C(object):
@staticmethod
def foo():
pass
Brak kontekstu w nowym opakowanym CPO foonie generuje błędu i można to zweryfikować w następujący sposób:
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
Przypadek użycia metody statycznej jest bardziej przestrzenią nazw i utrzymywalnością kodu (usunięcie go z klasy i udostępnienie w całym module itp.).
Może lepiej jest pisać metody statyczne niż metody instancji, o ile to możliwe, chyba że oczywiście trzeba kontekstualizować metody (takie jak zmienne instancji dostępu, zmienne klas itp.). Jednym z powodów jest ułatwienie wyrzucania elementów bezużytecznych przez niepotrzebne odwoływanie się do obiektów.