Czy możliwe jest posiadanie statycznych metod w Pythonie, które mógłbym wywołać bez inicjowania klasy, takich jak:
ClassName.static_method()
Czy możliwe jest posiadanie statycznych metod w Pythonie, które mógłbym wywołać bez inicjowania klasy, takich jak:
ClassName.static_method()
Odpowiedzi:
Tak, używając dekoratora staticmethod
class MyClass(object):
@staticmethod
def the_static_method(x):
print(x)
MyClass.the_static_method(2) # outputs 2
Zauważ, że w niektórych kodach może być używana stara metoda definiowania metody statycznej, używana staticmethod
raczej jako funkcja niż dekorator. Należy tego używać tylko wtedy, gdy musisz obsługiwać starsze wersje Pythona (2.2 i 2.3)
class MyClass(object):
def the_static_method(x):
print(x)
the_static_method = staticmethod(the_static_method)
MyClass.the_static_method(2) # outputs 2
Jest to całkowicie identyczne z pierwszym przykładem (używanie @staticmethod
), po prostu nie używanie ładnej składni dekoratora
Wreszcie, używaj staticmethod()
oszczędnie! Istnieje bardzo niewiele sytuacji, w których w Pythonie potrzebne są metody statyczne i widziałem, że były one używane wiele razy, w których oddzielna funkcja „najwyższego poziomu” byłaby jaśniejsza.
Z dokumentacji wynika dosłownie :
Metoda statyczna nie otrzymuje domyślnego pierwszego argumentu. Aby zadeklarować metodę statyczną, użyj tego idiomu:
class C: @staticmethod def f(arg1, arg2, ...): ...
Forma @staticmethod jest dekoratorem funkcji - szczegółowe informacje można znaleźć w opisie definicji funkcji w Definicjach funkcji .
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ę, patrz
classmethod()
.Aby uzyskać więcej informacji na temat metod statycznych, zapoznaj się z dokumentacją dotyczącą standardowej hierarchii typów w sekcji Standardowa hierarchia typów .
Nowości w wersji 2.2.
Zmieniono w wersji 2.4: Dodano składnię dekoratora funkcji.
ClassName.methodName()
, jakby była statyczna, a następnie nie self
zostanie przekazana żadna metoda. Jak powiedziałeś, nadal będzie można wywołać tę metodę jako ClassInstance.methodName()
i self
będzie ona podawana jako pierwszy parametr, niezależnie od jego nazwy.
Myślę, że Steven ma rację . Aby odpowiedzieć na pierwotne pytanie, w celu ustawienia metody klasy po prostu załóż, że pierwszy argument nie będzie instancją wywołującą, a następnie upewnij się, że metoda wywoływana jest tylko z klasy.
(Zauważ, że ta odpowiedź odnosi się do Pythona 3.x. W Pythonie 2.x otrzymasz TypeError
wywołanie metody na samej klasie).
Na przykład:
class Dog:
count = 0 # this is a class variable
dogs = [] # this is a class variable
def __init__(self, name):
self.name = name #self.name is an instance variable
Dog.count += 1
Dog.dogs.append(name)
def bark(self, n): # this is an instance method
print("{} says: {}".format(self.name, "woof! " * n))
def rollCall(n): #this is implicitly a class method (see comments below)
print("There are {} dogs.".format(Dog.count))
if n >= len(Dog.dogs) or n < 0:
print("They are:")
for dog in Dog.dogs:
print(" {}".format(dog))
else:
print("The dog indexed at {} is {}.".format(n, Dog.dogs[n]))
fido = Dog("Fido")
fido.bark(3)
Dog.rollCall(-1)
rex = Dog("Rex")
Dog.rollCall(0)
W tym kodzie metoda „rollCall” zakłada, że pierwszy argument nie jest instancją (tak jakby był wywołany przez instancję zamiast klasy). Dopóki „rollCall” jest wywoływany z klasy, a nie z instancji, kod będzie działał poprawnie. Jeśli spróbujemy wywołać „rollCall” z instancji, np .:
rex.rollCall(-1)
spowodowałoby to jednak wyjątek, ponieważ wysłałby dwa argumenty: siebie i -1, a „rollCall” jest zdefiniowany tak, aby akceptował tylko jeden argument.
Nawiasem mówiąc, rex.rollCall () wyśle poprawną liczbę argumentów, ale spowoduje także zgłoszenie wyjątku, ponieważ teraz n będzie reprezentował instancję Dog (tj. Rex), gdy funkcja spodziewa się, że n będzie liczbą.
W tym momencie pojawia się dekoracja: jeśli poprzedzimy metodę „rollCall”
@staticmethod
następnie, stwierdzając wprost, że metoda jest statyczna, możemy nawet wywołać ją z instancji. Teraz,
rex.rollCall(-1)
pracowałbym. Wstawienie @staticmethod przed definicją metody powstrzymuje instancję przed wysłaniem się jako argumentu.
Możesz to sprawdzić, wypróbowując poniższy kod z komentarzem i bez linii @staticmethod.
class Dog:
count = 0 # this is a class variable
dogs = [] # this is a class variable
def __init__(self, name):
self.name = name #self.name is an instance variable
Dog.count += 1
Dog.dogs.append(name)
def bark(self, n): # this is an instance method
print("{} says: {}".format(self.name, "woof! " * n))
@staticmethod
def rollCall(n):
print("There are {} dogs.".format(Dog.count))
if n >= len(Dog.dogs) or n < 0:
print("They are:")
for dog in Dog.dogs:
print(" {}".format(dog))
else:
print("The dog indexed at {} is {}.".format(n, Dog.dogs[n]))
fido = Dog("Fido")
fido.bark(3)
Dog.rollCall(-1)
rex = Dog("Rex")
Dog.rollCall(0)
rex.rollCall(-1)
TypeError: unbound method rollCall() must be called with Dog instance as first argument (got int instance instead)
T.my_static_method()
lub type(my_t_instance).my_static_method()
, ponieważ jest to jaśniejsze i od razu widać, że wywołuję metodę statyczną.
Tak, sprawdź dekorator staticmethod :
>>> class C:
... @staticmethod
... def hello():
... print "Hello World"
...
>>> C.hello()
Hello World
Naprawdę nie musisz używać @staticmethod
dekoratora. Wystarczy zadeklarować metodę (która nie oczekuje parametru własnego) i wywołać ją z klasy. Dekorator jest tylko w przypadku, gdy chcesz mieć możliwość wywołania go również z instancji (czego nie chciałeś zrobić)
Najczęściej używasz tylko funkcji ...
class Dummy: def static1(): print "hello from static1" @staticmethod def static2(): print "hello from static2" Dummy.static2() Dummy.static1()
Wyjście: hello from static2 Traceback <ostatnie ostatnie wywołanie>: Plik „ll.py”, wiersz 46, w <module> Dummy.static1 () TypeError: metoda niezwiązana static1 () musi być wywołany z instancją Dummy jako pierwszy argument (zamiast tego nic nie dostał)
self
jako pierwszy argument, chyba że powiesz mu, żeby tego nie robił. (patrz: dekorator)
self
ref w zależności od tego, jak się nazywa. Przypadek testowy: pastebin.com/12DDV7DB .
staticmethod
Dekorator pozwala wywołać funkcję zarówno w klasie i instancji (rozwiązanie to nie powiedzie się podczas wywoływania funkcji w instancji).
class C: def callme(): print('called'); C.callme()
Metody statyczne w Pythonie?
Czy w Pythonie można mieć metody statyczne, aby móc je wywoływać bez inicjowania klasy, na przykład:
ClassName.StaticMethod()
Tak, metody statyczne można tworzyć w ten sposób (chociaż nieco bardziej Pythonic używa do podkreślania metod zamiast CamelCase):
class ClassName(object):
@staticmethod
def static_method(kwarg1=None):
'''return a value that is a function of kwarg1'''
Powyżej używa składni dekoratora. Ta składnia jest równoważna z
class ClassName(object):
def static_method(kwarg1=None):
'''return a value that is a function of kwarg1'''
static_method = staticmethod(static_method)
Można tego użyć zgodnie z opisem:
ClassName.static_method()
Wbudowany przykład metody statycznej znajduje się str.maketrans()
w Pythonie 3, który był funkcją w string
module w Pythonie 2.
Inną opcją, której można użyć podczas opisywania, jest to classmethod
, że różnica polega na tym, że metoda class pobiera klasę jako domyślny pierwszy argument, a jeśli jest podklasą, otrzymuje podklasę jako domyślny pierwszy argument.
class ClassName(object):
@classmethod
def class_method(cls, kwarg1=None):
'''return a value that is a function of the class and kwarg1'''
Zauważ, że cls
nie jest to wymagana nazwa pierwszego argumentu, ale większość doświadczonych programistów Pythona uzna to za źle zrobione, jeśli użyjesz czegoś innego.
Są one zwykle używane jako alternatywne konstruktory.
new_instance = ClassName.class_method()
Wbudowanym przykładem jest dict.fromkeys()
:
new_dict = dict.fromkeys(['key1', 'key2'])
Poza szczególnymi cechami zachowania się obiektów metod statycznych , istnieje pewien rodzaj piękna, z którym możesz się nimi zaatakować, jeśli chodzi o organizację kodu na poziomie modułu.
# garden.py
def trim(a):
pass
def strip(a):
pass
def bunch(a, b):
pass
def _foo(foo):
pass
class powertools(object):
"""
Provides much regarded gardening power tools.
"""
@staticmethod
def answer_to_the_ultimate_question_of_life_the_universe_and_everything():
return 42
@staticmethod
def random():
return 13
@staticmethod
def promise():
return True
def _bar(baz, quux):
pass
class _Dice(object):
pass
class _6d(_Dice):
pass
class _12d(_Dice):
pass
class _Smarter:
pass
class _MagicalPonies:
pass
class _Samurai:
pass
class Foo(_6d, _Samurai):
pass
class Bar(_12d, _Smarter, _MagicalPonies):
pass
...
# tests.py
import unittest
import garden
class GardenTests(unittest.TestCase):
pass
class PowertoolsTests(unittest.TestCase):
pass
class FooTests(unittest.TestCase):
pass
class BarTests(unittest.TestCase):
pass
...
# interactive.py
from garden import trim, bunch, Foo
f = trim(Foo())
bunch(f, Foo())
...
# my_garden.py
import garden
from garden import powertools
class _Cowboy(garden._Samurai):
def hit():
return powertools.promise() and powertools.random() or 0
class Foo(_Cowboy, garden.Foo):
pass
Staje się teraz nieco bardziej intuicyjny i samok dokumentujący się w kontekście, w którym mają być używane określone komponenty, i idealnie nadaje się do nazwania odrębnych przypadków testowych, a także ma proste podejście do sposobu mapowania modułów testowych na moduły testowane dla purystów .
Często uważam, że zastosowanie tego podejścia do organizacji kodu narzędziowego projektu jest opłacalne. Dość często ludzie natychmiast spieszą się i tworzą utils
pakiet i kończą z 9 modułami, z których jeden ma 120 LOC, a reszta to w najlepszym razie dwa tuziny LOC. Wolę zacząć od tego i przekształcić go w pakiet i tworzyć moduły tylko dla bestii, które naprawdę na nie zasługują:
# utils.py
class socket(object):
@staticmethod
def check_if_port_available(port):
pass
@staticmethod
def get_free_port(port)
pass
class image(object):
@staticmethod
def to_rgb(image):
pass
@staticmethod
def to_cmyk(image):
pass
Być może najprostszą opcją jest po prostu umieszczenie tych funkcji poza klasą:
class Dog(object):
def __init__(self, name):
self.name = name
def bark(self):
if self.name == "Doggy":
return barking_sound()
else:
return "yip yip"
def barking_sound():
return "woof woof"
Za pomocą tej metody funkcje, które modyfikują lub wykorzystują wewnętrzny stan obiektu (wywołują skutki uboczne) mogą być przechowywane w klasie, a funkcje narzędziowe wielokrotnego użytku można przenieść na zewnątrz.
Powiedzmy, że ten plik się nazywa dogs.py
. Aby ich użyć, zadzwoń dogs.barking_sound()
zamiast dogs.Dog.barking_sound
.
Jeśli naprawdę potrzebujesz metody statycznej, aby być częścią klasy, możesz użyć dekoratora staticmethod .
Tak więc metody statyczne to metody, które można wywoływać bez tworzenia obiektu klasy. Na przykład :-
@staticmethod
def add(a, b):
return a + b
b = A.add(12,12)
print b
W powyższym przykładzie metoda add
jest wywoływana przez nazwę klasy, a A
nie nazwę obiektu.
Metody statyczne w języku Python można tworzyć na dwa sposoby.
Używanie staticmethod ()
class Arithmetic:
def add(x, y):
return x + y
# create add static method
Arithmetic.add = staticmethod(Arithmetic.add)
print('Result:', Arithmetic.add(15, 10))
Wynik:
Wynik: 25
Korzystanie z @staticmethod
class Arithmetic:
# create add static method
@staticmethod
def add(x, y):
return x + y
print('Result:', Arithmetic.add(15, 10))
Wynik:
Wynik: 25
Od czasu do czasu napotykam to pytanie. Przypadek użycia i przykład, który lubię to:
jeffs@jeffs-desktop:/home/jeffs $ python36
Python 3.6.1 (default, Sep 7 2017, 16:36:03)
[GCC 6.3.0 20170406] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cmath
>>> print(cmath.sqrt(-4))
2j
>>>
>>> dir(cmath)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh', 'cos', 'cosh', 'e', 'exp', 'inf', 'infj', 'isclose', 'isfinite', 'isinf', 'isnan', 'log', 'log10', 'nan', 'nanj', 'phase', 'pi', 'polar', 'rect', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau']
>>>
Nie ma sensu tworzyć obiektów klasy cmath, ponieważ nie ma stanu w obiekcie cmath. Jednak cmath to zbiór metod, które są w jakiś sposób powiązane. W powyższym przykładzie wszystkie funkcje cmath działają w pewien sposób na liczbach zespolonych.
@staticmethod
dekoracje lub używać wskaźnika funkcji, skoro można po prostu uniknąć pierwszegoself
parametru? Cóż, dla obiektua
nie będziesz mógł wywoływaća.your_static_method()
, co jest dozwolone w innych językach, ale i tak jest to uważane za złą praktykę, a kompilator zawsze ostrzega przed tym