Kiedy próbuję użyć metody statycznej z treści klasy i zdefiniować metodę statyczną za pomocą funkcji wbudowanej staticmethod
jako dekoratora, na przykład:
class Klass(object):
@staticmethod # use as decorator
def _stat_func():
return 42
_ANS = _stat_func() # call the staticmethod
def method(self):
ret = Klass._stat_func() + Klass._ANS
return ret
Otrzymuję następujący błąd:
Traceback (most recent call last):<br>
File "call_staticmethod.py", line 1, in <module>
class Klass(object):
File "call_staticmethod.py", line 7, in Klass
_ANS = _stat_func()
TypeError: 'staticmethod' object is not callable
Rozumiem, dlaczego tak się dzieje (wiązanie deskryptora) i mogę to obejść, ręcznie konwertując _stat_func()
na metodę statyczną po jej ostatnim użyciu, na przykład:
class Klass(object):
def _stat_func():
return 42
_ANS = _stat_func() # use the non-staticmethod version
_stat_func = staticmethod(_stat_func) # convert function to a static method
def method(self):
ret = Klass._stat_func() + Klass._ANS
return ret
Więc moje pytanie brzmi:
Czy są lepsze, jak w czystszym lub bardziej „Pythonic”, sposoby osiągnięcia tego celu?
python
decorator
static-methods
martineau
źródło
źródło
staticmethod
. Zwykle są bardziej przydatne jako funkcje na poziomie modułu, w takim przypadku twój problem nie jest problemem.classmethod
, z drugiej strony ...Odpowiedzi:
staticmethod
obiekty najwyraźniej mają__func__
atrybut przechowujący oryginalną funkcję surową (ma sens, że musiały). Więc to zadziała:Na marginesie, chociaż podejrzewałem, że obiekt metody static ma jakiś atrybut przechowujący oryginalną funkcję, nie miałem pojęcia o szczegółach. W duchu uczenia kogoś, jak łowić, zamiast dawać mu rybę, oto co zrobiłem, aby to zbadać i dowiedzieć się (C&P z mojej sesji w Pythonie):
Podobne rodzaje przekopywania się podczas sesji interaktywnej (
dir
jest to bardzo pomocne) często mogą bardzo szybko rozwiązać tego rodzaju pytania.źródło
__func__
to tylko inna nazwa dlaim_func
Py 2.6, która została dodana do Pythona 3 w celu zapewnienia zgodności w przód.__func__
Atrybut metody statycznej zapewnia odniesienie do oryginalnej funkcji, dokładnie tak, jakbyś nigdy nie używałstaticmethod
dekoratora. Więc jeśli twoja funkcja wymaga argumentów, będziesz musiał je przekazać podczas wywoływania__func__
. Komunikat o błędzie brzmi tak, jakbyś nie podał żadnych argumentów. Jeśli przykładstat_func
w tym poście przyjął dwa argumenty,_ANS = stat_func.__func__(arg1, arg2)
stat_func
sama jest taką zmienną). Czy chodziło Ci o to, że nie możesz używać atrybutów instancji klasy, którą definiujesz? To prawda, ale nic dziwnego; nie mamy instancji tej klasy, ponieważ wciąż definiujemy klasę! Ale tak czy inaczej, miałem na myśli ich tylko jako odpowiedniki wszelkich argumentów, które chciałeś przekazać; można tam użyć literałów.Tak wolę:
Wolę to rozwiązanie niż
Klass.stat_func
ze względu na zasadę DRY . Przypomina mi powód, dla którego pojawił się nowysuper()
w Pythonie 3 :)Ale zgadzam się z innymi, zwykle najlepszym wyborem jest zdefiniowanie funkcji na poziomie modułu.
Na przykład w przypadku
@staticmethod
funkcji rekurencja może nie wyglądać zbyt dobrze (musiałbyś złamać zasadę DRY, dzwoniąc doKlass.stat_func
środkaKlass.stat_func
). Dzieje się tak, ponieważ nie masz odwołania doself
wewnętrznej metody statycznej. Dzięki funkcji na poziomie modułu wszystko będzie wyglądać dobrze.źródło
self.__class__.stat_func()
w zwykłych metodach ma zalety (DRY i wszystko inne) w porównaniu z używaniemKlass.stat_func()
, to nie było tak naprawdę tematem mojego pytania - w rzeczywistości unikałem używania tego pierwszego, aby nie zaciemnić problemu niespójności.self.__class__
jest lepsze, ponieważ jeśli podklasa ma pierwszeństwostat_func
,Subclass.method
wywoła podklasęstat_func
. Ale żeby być całkowicie szczerym, w takiej sytuacji o wiele lepiej jest po prostu użyć prawdziwej metody zamiast statycznej.A co z wstrzyknięciem atrybutu class po definicji klasy?
źródło
Wynika to z faktu, że metoda staticmethod jest deskryptorem i wymaga pobrania atrybutu na poziomie klasy w celu wykonania protokołu deskryptora i uzyskania prawdziwego możliwego do wywołania.
Z kodu źródłowego:
Ale nie bezpośrednio z wnętrza klasy podczas jej definiowania.
Ale jak wspomniał jeden z komentatorów, to wcale nie jest projekt „Pythonic”. Zamiast tego użyj funkcji na poziomie modułu.
źródło
A co z tym rozwiązaniem? Nie polega na znajomości
@staticmethod
implementacji dekoratora. Klasa wewnętrzna StaticMethod odgrywa rolę kontenera statycznych funkcji inicjalizacyjnych.źródło
__func__
ponieważ jest to teraz oficjalnie udokumentowane (zobacz sekcję Inne zmiany językowe nowości w Pythonie 2.7 i jej odniesienie do wydania 5982 ). Twoje rozwiązanie jest jeszcze bardziej przenośne, ponieważ prawdopodobnie działałoby również w wersjach Pythona wcześniejszych niż 2.6 (kiedy__func__
zostało po raz pierwszy wprowadzone jako synonimim_func
).