Jaka jest różnica między funkcją ozdobione @staticmethod
i jeden ozdobione @classmethod
?
Jaka jest różnica między funkcją ozdobione @staticmethod
i jeden ozdobione @classmethod
?
Odpowiedzi:
Może trochę kodu przykładu pomoże: Zauważ różnicę w podpisach pożarowych foo
, class_foo
oraz static_foo
:
class A(object):
def foo(self, x):
print "executing foo(%s, %s)" % (self, x)
@classmethod
def class_foo(cls, x):
print "executing class_foo(%s, %s)" % (cls, x)
@staticmethod
def static_foo(x):
print "executing static_foo(%s)" % x
a = A()
Poniżej znajduje się zwykły sposób, w jaki instancja obiektu wywołuje metodę. Instancja obiektu a
, jest domyślnie przekazywana jako pierwszy argument.
a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)
W przypadku metod klasy klasa instancji obiektu jest domyślnie przekazywana jako pierwszy argument zamiast self
.
a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
Możesz także zadzwonić class_foo
za pomocą klasy. W rzeczywistości, jeśli zdefiniujesz coś jako metodę klasy, prawdopodobnie dzieje się tak dlatego, że zamierzasz wywoływać to z klasy, a nie z instancji klasy. A.foo(1)
zgłosiłby błąd typu TypeError, ale A.class_foo(1)
działa dobrze:
A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
Jednym z zastosowań, które ludzie znaleźli dla metod klasowych, jest tworzenie dziedziczonych alternatywnych konstruktorów .
W przypadku metod statycznych ani self
(instancja obiektu), ani cls
(klasa) nie są domyślnie przekazywane jako pierwszy argument. Zachowują się jak zwykłe funkcje, tyle że można je wywoływać z instancji lub klasy:
a.static_foo(1)
# executing static_foo(1)
A.static_foo('hi')
# executing static_foo(hi)
Metody statyczne są używane do grupowania funkcji, które mają pewne logiczne połączenie z klasą do klasy.
foo
jest po prostu funkcją, ale kiedy wywołujesz a.foo
, nie dostajesz po prostu funkcji, dostajesz „częściowo zastosowaną” wersję funkcji z instancją obiektu a
związaną jako pierwszy argument funkcji. foo
oczekuje 2 argumentów, a a.foo
tylko 1 argument.
a
jest związany z foo
. Oto, co należy rozumieć pod pojęciem „związany” poniżej:
print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>
Dzięki a.class_foo
, a
nie jest związany class_foo
, a klasa A
jest związana class_foo
.
print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>
Tutaj przy pomocy metody statycznej, mimo że jest to metoda, a.static_foo
zwraca po prostu dobrą funkcję „ole” bez żadnych argumentów. static_foo
oczekuje 1 argumentu i
a.static_foo
oczekuje również 1 argumentu.
print(a.static_foo)
# <function static_foo at 0xb7d479cc>
I oczywiście to samo dzieje się, gdy zamiast tego dzwonisz static_foo
do klasy A
.
print(A.static_foo)
# <function static_foo at 0xb7d479cc>
@staticmethod
może pomóc uporządkować kod, zastępując go podklasami. Bez niego warianty funkcji będą się unosić w przestrzeni nazw modułów.
@staticmethod
- możesz go użyć, aby usunąć cruft. Wdrażam język programowania w Pythonie - funkcje zdefiniowane w bibliotece używają execute
metody statycznej , w której funkcje zdefiniowane przez użytkownika wymagają argumentów instancji (tj. Treści funkcji). Ten dekorator eliminuje ostrzeżenia „nieużywanego parametru” w inspektorze PyCharm.
Staticmethod jest metodą, która nie wie nic na temat klasy lub instancji był nazywany dalej. Dostaje tylko argumenty, które zostały przekazane, bez domyślnego pierwszego argumentu. Jest to w zasadzie bezużyteczne w Pythonie - możesz po prostu użyć funkcji modułu zamiast metody static.
Classmethod , z drugiej strony, jest to metoda, która zostaje przekazana klasę Nazwano na lub klasę instancji był nazywany dalej, jako pierwszy argument. Jest to przydatne, gdy chcesz, aby metoda była fabryką dla klasy: ponieważ pobiera rzeczywistą klasę, do której została wywołana, jako pierwszy argument, zawsze możesz utworzyć odpowiednią klasę, nawet jeśli w grę wchodzą podklasy. Obserwuj na przykład, w jaki dict.fromkeys()
sposób metoda klasy zwraca instancję podklasy po wywołaniu w podklasie:
>>> class DictSubclass(dict):
... def __repr__(self):
... return "DictSubclass"
...
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>
Zasadniczo @classmethod
tworzy metodę, której pierwszym argumentem jest klasa, z której została wywołana (a nie instancja klasy), @staticmethod
nie ma żadnych domyślnych argumentów.
Oficjalne dokumenty Pythona:
Metoda klasy odbiera klasę jako domyślny pierwszy argument, podobnie jak metoda instancji odbiera instancję. Aby zadeklarować metodę klasową, użyj tego idiomu:
class C: @classmethod def f(cls, arg1, arg2, ...): ...
@classmethod
Forma jest funkcją dekorator - patrz opis definicji funkcji w definicji funkcji o szczegóły.Można go wywołać w klasie (np.
C.f()
) Lub w instancji (np.C().f()
). Instancja jest ignorowana z wyjątkiem swojej klasy. Jeśli dla klasy pochodnej wywoływana jest metoda klasy, obiekt klasy pochodnej jest przekazywany jako domyślny pierwszy argument.Metody klasowe różnią się od metod statycznych C ++ lub Java. Jeśli chcesz, zobacz
staticmethod()
w tej sekcji.
Metoda statyczna nie otrzymuje domyślnego pierwszego argumentu. Aby zadeklarować metodę statyczną, użyj tego idiomu:
class C: @staticmethod def f(arg1, arg2, ...): ...
@staticmethod
Forma jest funkcją dekorator - patrz opis definicji funkcji w definicji funkcji o szczegóły.Można go wywołać w klasie (np.
C.f()
) Lub w instancji (np.C().f()
). Instancja jest ignorowana z wyjątkiem swojej klasy.Metody statyczne w Pythonie są podobne do metod znalezionych w Javie lub C ++. Bardziej zaawansowaną koncepcję można znaleźć
classmethod()
w tej sekcji.
Oto krótki artykuł na ten temat
Funkcja @staticmethod jest niczym więcej niż funkcją zdefiniowaną w klasie. Można to wywoływać bez wcześniejszego tworzenia klasy. Jego definicja jest niezmienna przez dziedziczenie.
Funkcja @classmethod można również wywoływać bez tworzenia instancji klasy, ale jej definicja jest zgodna z podklasą, a nie klasą nadrzędną, poprzez dziedziczenie. Jest tak, ponieważ pierwszym argumentem dla funkcji @classmethod zawsze musi być cls (klasa).
Aby zdecydować, czy użyć @staticmethod czy @classmethod , musisz zajrzeć do swojej metody. Jeśli twoja metoda uzyskuje dostęp do innych zmiennych / metod w twojej klasie, użyj @classmethod . Z drugiej strony, jeśli twoja metoda nie dotyka żadnych innych części klasy, użyj @staticmethod.
class Apple:
_counter = 0
@staticmethod
def about_apple():
print('Apple is good for you.')
# note you can still access other member of the class
# but you have to use the class instance
# which is not very nice, because you have repeat yourself
#
# For example:
# @staticmethod
# print('Number of apples have been juiced: %s' % Apple._counter)
#
# @classmethod
# print('Number of apples have been juiced: %s' % cls._counter)
#
# @classmethod is especially useful when you move your function to other class,
# you don't have to rename the class reference
@classmethod
def make_apple_juice(cls, number_of_apples):
print('Make juice:')
for i in range(number_of_apples):
cls._juice_this(i)
@classmethod
def _juice_this(cls, apple):
print('Juicing %d...' % apple)
cls._counter += 1
cls._counter
nadal będzie, cls._counter
nawet jeśli kod zostanie umieszczony w innej klasie lub nazwa klasy zostanie zmieniona. Apple._counter
jest specyficzny dla Apple
klasy; dla innej klasy lub gdy nazwa klasy zostanie zmieniona, należy zmienić klasę odniesienia.
Jaka jest różnica między @staticmethod i @classmethod w Pythonie?
Być może widziałeś kod Pythona, taki jak ten pseudokod, który demonstruje sygnatury różnych typów metod i udostępnia dokumentację wyjaśniającą każdy z nich:
class Foo(object):
def a_normal_instance_method(self, arg_1, kwarg_2=None):
'''
Return a value that is a function of the instance with its
attributes, and other arguments such as arg_1 and kwarg2
'''
@staticmethod
def a_static_method(arg_0):
'''
Return a value that is a function of arg_0. It does not know the
instance or class it is called from.
'''
@classmethod
def a_class_method(cls, arg1):
'''
Return a value that is a function of the class and other arguments.
respects subclassing, it is called with the class it is called from.
'''
Najpierw wyjaśnię a_normal_instance_method
. Jest to dokładnie nazywane „ metodą instancji ”. Gdy używana jest metoda instancji, jest ona używana jako funkcja częściowa (w przeciwieństwie do funkcji całkowitej, zdefiniowanej dla wszystkich wartości podczas przeglądania w kodzie źródłowym), która, gdy jest używana, pierwszy z argumentów jest predefiniowana jako instancja obiekt ze wszystkimi podanymi atrybutami. Jest z nim związana instancja obiektu i musi być wywołana z instancji obiektu. Zwykle uzyskuje dostęp do różnych atrybutów instancji.
Na przykład jest to instancja ciągu:
', '
jeśli użyjemy metody instancji join
na tym ciągu, aby dołączyć do innej iterowalnej, jest to oczywiście funkcja instancji, oprócz tego, że jest funkcją listy iterowalnej ['a', 'b', 'c']
:
>>> ', '.join(['a', 'b', 'c'])
'a, b, c'
Metody instancji można powiązać za pomocą wyszukiwania przerywanego do późniejszego użycia.
Na przykład wiąże to str.join
metodę z ':'
instancją:
>>> join_with_colons = ':'.join
Później możemy użyć tego jako funkcji, która ma już przypisany pierwszy argument. W ten sposób działa jak funkcja częściowa w instancji:
>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'
Metoda statyczna nie przyjmuje instancji jako argumentu.
Jest bardzo podobny do funkcji na poziomie modułu.
Jednak funkcja poziomu modułu musi znajdować się w module i być specjalnie importowana do innych miejsc, w których jest używana.
Jednak jeśli jest on dołączony do obiektu, będzie wygodnie podążał za obiektem również poprzez importowanie i dziedziczenie.
Przykładem metody statycznej jest str.maketrans
przeniesienie z string
modułu w Pythonie 3. To sprawia, że tabela translacji nadaje się do użycia przez str.translate
. Wydaje się dość głupie, gdy jest używane z instancji ciągu, jak pokazano poniżej, ale importowanie funkcji z string
modułu jest raczej niezdarne i fajnie jest móc wywoływać ją z klasy, jak wstr.maketrans
# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
W Pythonie 2 musisz zaimportować tę funkcję z coraz mniej użytecznego modułu łańcuchowego:
>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'
Metoda klasy jest podobna do metody instancji, ponieważ przyjmuje domyślny pierwszy argument, ale zamiast przejmować instancję, bierze klasę. Często są one używane jako alternatywne konstruktory dla lepszego wykorzystania semantycznego i będą wspierać dziedziczenie.
Najbardziej kanonicznym przykładem wbudowanej metody klasy jest dict.fromkeys
. Jest używany jako alternatywny konstruktor dict (dobrze nadaje się, gdy wiesz, jakie są twoje klucze i chcesz dla nich wartość domyślną).
>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}
Kiedy dyktujemy podklasę, możemy użyć tego samego konstruktora, który tworzy instancję podklasy.
>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>
Zobacz kod źródłowy pandy dla innych podobnych przykładów alternatywnych konstruktorów, a także oficjalną dokumentację Pythona na temat classmethod
i staticmethod
.
Zacząłem uczyć się języka programowania w C ++, potem w Javie, a potem w Pythonie, więc to pytanie również mnie bardzo niepokoiło, dopóki nie zrozumiałem prostego użycia każdego z nich.
Metoda klasy: Python w przeciwieństwie do Java i C ++ nie ma przeciążenia konstruktora. Aby to osiągnąć, możesz użyć classmethod
. Poniższy przykład to wyjaśni
Rozważmy mamy Person
klasę która przyjmuje dwa argumenty first_name
a last_name
i tworzy instancję Person
.
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
Jeśli wymaganie pojawi się wtedy, gdy musisz utworzyć klasę używając tylko jednej nazwy, po prostu first_name
nie możesz zrobić czegoś takiego w Pythonie.
Spowoduje to błąd podczas próby utworzenia obiektu (instancji).
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def __init__(self, first_name):
self.first_name = first_name
Można jednak osiągnąć to samo, @classmethod
jak wspomniano poniżej
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@classmethod
def get_person(cls, first_name):
return cls(first_name, "")
Metoda statyczna: jest to dość proste, nie jest powiązane z instancją ani klasą i można to po prostu wywołać za pomocą nazwy klasy.
Powiedzmy, że w powyższym przykładzie potrzebujesz walidacji, która first_name
nie powinna przekraczać 20 znaków, możesz to po prostu zrobić.
@staticmethod
def validate_name(name):
return len(name) <= 20
i możesz po prostu zadzwonić za pomocą class name
Person.validate_name("Gaurang Shah")
def __init__(self, first_name, last_name="")
zamiast metody klasowej get_person
. Również wynik będzie dokładnie taki sam w tym przypadku.
Wydaje mi się, że lepszym pytaniem jest „Kiedy użyłbyś @classmethod vs. @staticmethod?”
@classmethod pozwala na łatwy dostęp do prywatnych członków powiązanych z definicją klasy. jest to świetny sposób na tworzenie singletonów lub istnieją klasy fabryczne, które kontrolują liczbę instancji tworzonych obiektów.
@staticmethod zapewnia marginalny wzrost wydajności, ale nie widziałem jeszcze produktywnego zastosowania metody statycznej w klasie, której nie można byłoby uzyskać jako samodzielnej funkcji poza klasą.
W Pythonie 2.4 dodano @dekoratory. Jeśli używasz Pythona <2.4, możesz użyć funkcji classmethod () i staticmethod ().
Na przykład, jeśli chcesz utworzyć metodę fabryczną (funkcja zwracająca instancję innej implementacji klasy w zależności od otrzymanego argumentu), możesz zrobić coś takiego:
class Cluster(object):
def _is_cluster_for(cls, name):
"""
see if this class is the cluster with this name
this is a classmethod
"""
return cls.__name__ == name
_is_cluster_for = classmethod(_is_cluster_for)
#static method
def getCluster(name):
"""
static factory method, should be in Cluster class
returns a cluster object for the given name
"""
for cls in Cluster.__subclasses__():
if cls._is_cluster_for(name):
return cls()
getCluster = staticmethod(getCluster)
Zauważ również, że jest to dobry przykład użycia metody klasy i metody statycznej. Metoda statyczna wyraźnie należy do klasy, ponieważ korzysta ona wewnętrznie z Klastra klas. Metoda klasy potrzebuje tylko informacji o klasie, a nie instancji obiektu.
Kolejną zaletą przekształcenia _is_cluster_for
metody w metodę klasy jest to, że podklasa może zdecydować o zmianie implementacji, być może dlatego, że jest dość ogólna i może obsługiwać więcej niż jeden typ klastra, więc samo sprawdzenie nazwy klasy nie wystarczy.
Metody statyczne:
Korzyści z metod statycznych:
Wygodniej jest importować niż funkcje na poziomie modułu, ponieważ każda metoda nie musi być specjalnie importowana
@staticmethod
def some_static_method(*args, **kwds):
pass
Metody klasowe:
Są one tworzone za pomocą wbudowanej funkcji classmethod.
@classmethod
def some_class_method(cls, *args, **kwds):
pass
@staticmethod
po prostu wyłącza domyślną funkcję jako deskryptor metody. classmethod zawija twoją funkcję w wywoływalny kontener, który jako pierwszy argument przekazuje odwołanie do klasy będącej właścicielem:
>>> class C(object):
... pass
...
>>> def f():
... pass
...
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>
W rzeczywistości classmethod
ma narzut związany ze środowiskiem uruchomieniowym, ale umożliwia dostęp do klasy będącej właścicielem. Alternatywnie polecam użycie metaklasy i umieszczenie klasowych metod na tej metaklasie:
>>> class CMeta(type):
... def foo(cls):
... print cls
...
>>> class C(object):
... __metaclass__ = CMeta
...
>>> C.foo()
<class '__main__.C'>
c = C(); c.foo()
podnosi AttributeError, musisz zrobić type(c).foo()
. Może to być również uważane za cechę - nie mogę jednak wymyślić, dlaczego chcesz.
Ostateczny przewodnik na temat używania metod statycznych, klasowych lub abstrakcyjnych w Pythonie jest dobrym linkiem do tego tematu i podsumowano go następująco.
@staticmethod
funkcja jest niczym więcej niż funkcją zdefiniowaną w klasie. Można to wywoływać bez wcześniejszego tworzenia klasy. Jego definicja jest niezmienna przez dziedziczenie.
@classmethod
funkcja może być również wywoływana bez tworzenia instancji klasy, ale jej definicja jest następująca Podklasa, a nie klasa nadrzędna, poprzez dziedziczenie, może zostać zastąpiona przez podklasę. Jest tak, ponieważ pierwszym argumentem @classmethod
funkcji musi zawsze być cls (klasa).
Różni się tylko pierwszy argument :
Bardziej szczegółowo...
Gdy wywoływana jest metoda obiektu, automatycznie otrzymuje dodatkowy argument self
jako pierwszy argument. To znaczy metoda
def f(self, x, y)
należy wywołać z 2 argumentami. self
jest automatycznie przekazywany i jest to sam obiekt .
Kiedy metoda jest udekorowana
@classmethod
def f(cls, x, y)
automatycznie podawanym argumentem nie jest self
, ale klasa self
.
Kiedy metoda jest udekorowana
@staticmethod
def f(x, y)
metoda nie otrzymuje żadnego automatycznego argumentu. Podano tylko parametry, z którymi jest wywoływany.
classmethod
jest najczęściej używany do alternatywnych konstruktorów. staticmethod
nie używa stanu obiektu. Może to być funkcja zewnętrzna w stosunku do klasy. Umieszcza tylko wewnątrz klasy dla grupowania funkcji o podobnej funkcjonalności (na przykład, jak Math
metody statyczne klasy Java )class Point
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def frompolar(cls, radius, angle):
"""The `cls` argument is the `Point` class itself"""
return cls(radius * cos(angle), radius * sin(angle))
@staticmethod
def angle(x, y):
"""this could be outside the class, but we put it here
just because we think it is logically related to the class."""
return atan(y, x)
p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)
angle = Point.angle(3, 2)
Pozwól mi najpierw powiedzieć podobieństwo między metodą ozdobioną @classmethod a @staticmethod.
Podobieństwo: oba można wywoływać w samej klasie , a nie tylko w instancji klasy. Oba są więc w pewnym sensie metodami klasy .
Różnica: Metoda klasy otrzyma samą klasę jako pierwszy argument, podczas gdy metoda statyczna nie.
Zatem metoda statyczna nie jest, w pewnym sensie, związana z samą klasą i po prostu wisi w niej tylko dlatego, że może mieć powiązaną funkcjonalność.
>>> class Klaus:
@classmethod
def classmthd(*args):
return args
@staticmethod
def staticmthd(*args):
return args
# 1. Call classmethod without any arg
>>> Klaus.classmthd()
(__main__.Klaus,) # the class gets passed as the first argument
# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')
# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()
()
# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)
Innym aspektem dotyczącym metody statycznej vs metody klasowej jest dziedziczenie. Powiedz, że masz następującą klasę:
class Foo(object):
@staticmethod
def bar():
return "In Foo"
A następnie chcesz zastąpić bar()
w klasie podrzędnej:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
To działa, ale zauważ, że teraz bar()
implementacja w klasie potomnej ( Foo2
) nie może już korzystać z niczego specyficznego dla tej klasy. Na przykład powiedzmy Foo2
miał metodę o nazwie magic()
, której chcesz użyć w Foo2
implementacji bar()
:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
@staticmethod
def magic():
return "Something useful you'd like to use in bar, but now can't"
Rozwiązaniem tutaj byłoby zadzwonić Foo2.magic()
w bar()
, ale to jesteś powtarzając sobie (jeśli nazwaFoo2
zmian, trzeba pamiętać, aby zaktualizować tę bar()
metodę).
Dla mnie jest to niewielkie naruszenie zasady otwartego / zamkniętego , ponieważ podjęta decyzja Foo
ma wpływ na twoją zdolność do refaktoryzacji wspólnego kodu w klasie pochodnej (tj. Jest mniej otwarta na rozszerzenie). Gdybyśmy bar()
byli w classmethod
porządku:
class Foo(object):
@classmethod
def bar(cls):
return "In Foo"
class Foo2(Foo):
@classmethod
def bar(cls):
return "In Foo2 " + cls.magic()
@classmethod
def magic(cls):
return "MAGIC"
print Foo2().bar()
Daje: In Foo2 MAGIC
Spróbuję wyjaśnić podstawową różnicę na przykładzie.
class A(object):
x = 0
def say_hi(self):
pass
@staticmethod
def say_hi_static():
pass
@classmethod
def say_hi_class(cls):
pass
def run_self(self):
self.x += 1
print self.x # outputs 1
self.say_hi()
self.say_hi_static()
self.say_hi_class()
@staticmethod
def run_static():
print A.x # outputs 0
# A.say_hi() # wrong
A.say_hi_static()
A.say_hi_class()
@classmethod
def run_class(cls):
print cls.x # outputs 0
# cls.say_hi() # wrong
cls.say_hi_static()
cls.say_hi_class()
1 - możemy bezpośrednio wywoływać metody statyczne i klasowe bez inicjalizacji
# A.run_self() # wrong
A.run_static()
A.run_class()
2- Metoda statyczna nie może wywoływać metody self, ale może wywoływać inną metodę statyczną i klasową
3- Metoda statyczna należy do klasy i w ogóle nie będzie używać obiektu.
Metoda 4- klasy nie jest związana z obiektem, ale z klasą.
@classmethod: może zostać użyty do stworzenia wspólnego globalnego dostępu do wszystkich instancji utworzonych dla tej klasy ..... jak np. aktualizacja rekordu przez wielu użytkowników .... W szczególności uznałem, że jest on użyteczny przy tworzeniu singletonów ...: )
Metoda @static: nie ma nic wspólnego z klasą lub instancją powiązaną z ... ale dla czytelności można użyć metody statycznej
Warto rozważyć różnicę między:
Class A:
def foo(): # no self parameter, no decorator
pass
i
Class B:
@staticmethod
def foo(): # no self parameter
pass
Zmieniło się to między python2 i python3:
python2:
>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
python3:
>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
Tak więc używanie @staticmethod
metod wywoływanych tylko bezpośrednio z klasy stało się opcjonalne w python3. Jeśli chcesz wywoływać je zarówno z klasy, jak i instancji, nadal musisz użyć @staticmethod
dekoratora.
Pozostałe przypadki zostały dobrze uwzględnione w odpowiedzi Unutbus.
Metoda klasy odbiera klasę jako domyślny pierwszy argument, podobnie jak metoda instancji odbiera instancję. Jest to metoda powiązana z klasą, a nie z obiektem klasy. Ma ona dostęp do stanu klasy, ponieważ przyjmuje parametr klasy wskazujący na klasę, a nie na instancję obiektu. Może modyfikować stan klasy, który obowiązywałby we wszystkich instancjach klasy. Na przykład może modyfikować zmienną klasy, która będzie obowiązywać dla wszystkich instancji.
Z drugiej strony metoda statyczna nie otrzymuje domyślnego pierwszego argumentu w porównaniu do metod klasowych lub instancji. I nie może uzyskać dostępu ani modyfikować stanu klasy. Należy tylko do klasy, ponieważ z punktu widzenia projektowania jest to właściwy sposób. Ale pod względem funkcjonalności nie jest związana w czasie wykonywania z klasą.
jako wytyczne, użyj metod statycznych jako narzędzi, użyj metod klasowych, na przykład fabrycznie. A może zdefiniować singletona. I użyj metod instancji do modelowania stanu i zachowania instancji.
Mam nadzieję, że było jasne!
Mój wkład pokazuje różnicę wśród @classmethod
, @staticmethod
oraz metody instancji, w tym jak instancja może pośrednio wywołać @staticmethod
. Ale zamiast pośrednio wywoływać a @staticmethod
z instancji, uczynienie go prywatnym może być bardziej „pytoniczne”. Zdobycie czegoś z prywatnej metody nie zostało tutaj wykazane, ale jest to w zasadzie ta sama koncepcja.
#!python3
from os import system
system('cls')
# % % % % % % % % % % % % % % % % % % % %
class DemoClass(object):
# instance methods need a class instance and
# can access the instance through 'self'
def instance_method_1(self):
return 'called from inside the instance_method_1()'
def instance_method_2(self):
# an instance outside the class indirectly calls the static_method
return self.static_method() + ' via instance_method_2()'
# class methods don't need a class instance, they can't access the
# instance (self) but they have access to the class itself via 'cls'
@classmethod
def class_method(cls):
return 'called from inside the class_method()'
# static methods don't have access to 'cls' or 'self', they work like
# regular functions but belong to the class' namespace
@staticmethod
def static_method():
return 'called from inside the static_method()'
# % % % % % % % % % % % % % % % % % % % %
# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''
# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# % % % % % % % % % % % % % % % % % % % %
# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()
# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''
# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''
# call class_method()
print(democlassObj.class_method() + '\n')
''' called from inside the class_method() '''
# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''
"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""
Metody klasowe, jak sama nazwa wskazuje, służą do wprowadzania zmian do klas, a nie obiektów. Aby wprowadzić zmiany w klasach, zmodyfikują atrybuty klas (nie atrybuty obiektowe), ponieważ w ten sposób aktualizujesz klasy. To jest powód, dla którego metody klasowe przyjmują klasę (konwencjonalnie oznaczoną przez „cls”) jako pierwszy argument.
class A(object):
m=54
@classmethod
def class_method(cls):
print "m is %d" % cls.m
Z drugiej strony metody statyczne są wykorzystywane do wykonywania funkcji, które nie są związane z klasą, tj. Nie odczytują ani nie zapisują zmiennych klas. Stąd metody statyczne nie przyjmują klas jako argumentów. Są one używane, aby klasy mogły wykonywać funkcje niezwiązane bezpośrednio z celem klasy.
class X(object):
m=54 #will not be referenced
@staticmethod
def static_method():
print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."
Analizuj @staticmethod dosłownie dostarczając różnych informacji.
Normalna metoda klasy jest domyślną metodą dynamiczną , która przyjmuje instancję jako pierwszy argument.
W przeciwieństwie do tego metoda statyczna nie przyjmuje instancji jako pierwszego argumentu, dlatego nazywa się ją „statyczna” .
Metoda statyczna jest rzeczywiście taką normalną funkcją, jak te poza definicją klasy.
Na szczęście jest zgrupowane w klasie tylko po to, aby stać bliżej miejsca zastosowania lub możesz przewinąć, aby go znaleźć.
Myślę, że dając czysto Pythonową wersję staticmethod
iclassmethod
pomoże zrozumieć różnicę między nimi na poziomie języka.
Oba są deskryptorami innymi niż dane (łatwiej byłoby je zrozumieć, jeśli najpierw znasz deskryptory ).
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
return self.f
class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, cls=None):
def inner(*args, **kwargs):
if cls is None:
cls = type(obj)
return self.f(cls, *args, **kwargs)
return inner
staticmethod nie ma dostępu do atrybutów obiektu, klasy ani klas nadrzędnych w hierarchii dziedziczenia. Można go wywołać bezpośrednio w klasie (bez tworzenia obiektu).
classmethod nie ma dostępu do atrybutów obiektu. Może jednak uzyskać dostęp do atrybutów klasy i klas nadrzędnych w hierarchii dziedziczenia. Można go wywołać bezpośrednio w klasie (bez tworzenia obiektu). Wywołany w obiekcie jest taki sam, jak normalna metoda, która nie self.<attribute(s)>
uzyskuje dostępu i dostępuself.__class__.<attribute(s)>
tylko .
Pomyśl, że mamy klasę b=2
, stworzymy obiekt i ponownie w nim ustawimy b=4
. Metoda statyczna nie ma dostępu do niczego z poprzedniego. Classmethod może uzyskać dostęp .b==2
tylko poprzez cls.b
. Normalna metoda może uzyskać dostęp zarówno: .b==4
przez, jak self.b
i .b==2
przez self.__class__.b
.
Możemy postępować zgodnie ze stylem KISS (upraszczając, głupio): nie używaj metod static i metod, nie używaj klas bez ich tworzenia, uzyskuj dostęp tylko do atrybutów obiektu self.attribute(s)
. Istnieją języki, w których OOP jest wdrażany w ten sposób i myślę, że nie jest to zły pomysł. :)
Szybki hack innych identycznych metod w iPython ujawnia, że @staticmethod
daje marginalny wzrost wydajności (w nanosekundach), ale poza tym wydaje się, że nie spełnia żadnej funkcji. Ponadto wszelkie przyrosty wydajności zostaną prawdopodobnie usunięte przez dodatkowe prace związane z przetwarzaniem metodystaticmethod()
podczas kompilacji (co dzieje się przed wykonaniem kodu po uruchomieniu skryptu).
Ze względu na czytelność kodu unikałbym, @staticmethod
chyba że twoja metoda zostanie wykorzystana do prac, w których liczą się nanosekundy.