Metody statyczne w Pythonie?

1719

Czy możliwe jest posiadanie statycznych metod w Pythonie, które mógłbym wywołać bez inicjowania klasy, takich jak:

ClassName.static_method()
Joan Venge
źródło

Odpowiedzi:

2026

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 staticmethodraczej 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.

dbr
źródło
15
Po co dodawać @staticmethoddekoracje lub używać wskaźnika funkcji, skoro można po prostu uniknąć pierwszego selfparametru? Cóż, dla obiektu anie 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
SomethingSomething
1
Korzystam z Pythona 2.7.12, z dekoratorem @staticmethod nie mogę wywołać pierwszej zdefiniowanej metody statycznej. Występuje błąd: TypeError: Obiekt „staticmethod” nie jest
możliwy do wywołania
Supratim Samantray, jesteś pewien? ponieważ tutaj wyraźnie stwierdzono, że można go używać po wersji 2.4.
realslimshanky
11
@SomethingSomething, jeśli nie użyjesz nazwy zmiennej „self”, ale nie dodasz dekoratora, pierwszy argument, np. Arg1, będzie nadal odwołaniem do instancji self. Nazwa „ja” to tylko konwencja.
George Moutsopoulos,
1
@GeorgeMoutsopoulos - ogólnie uzgodnione. Ale - i tak będziesz mógł wywołać metodę tak ClassName.methodName(), jakby była statyczna, a następnie nie selfzostanie przekazana żadna metoda. Jak powiedziałeś, nadal będzie można wywołać tę metodę jako ClassInstance.methodName()i selfbędzie ona podawana jako pierwszy parametr, niezależnie od jego nazwy.
Coś się
220

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 TypeErrorwywoł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)
Richard Ambler
źródło
8
Pierwszy przykład (wywołanie według klasy bez @staticmethod) nie działa dla mnie w Pythonie 2.7. Otrzymuję komunikat „TypeError: niezwiązana metoda rollCall () musi zostać wywołany z instancją Dog jako pierwszy argument (zamiast tego dostał instancję int)”
tba
2
To nie działa. Zarówno Dog.rollCall (-1), jak i rex.rollCall (-1) zwracają to samoTypeError: unbound method rollCall() must be called with Dog instance as first argument (got int instance instead)
Mittenchops
3
@MestreLion Myślę, że nie jest to ogromna korzyść, ponieważ wszystko to robi zamęt metod statycznych i instancji i sprawia, że ​​jeden wygląda jak drugi. Jeśli wiesz, że metoda jest statyczna, powinieneś uzyskać do niej dostęp jako metodę statyczną. Fakt, że metoda nie potrzebuje ani nie używa instancji, powinien być oczywisty niezależnie od tego, gdzie używasz tej metody. Zawsze używam 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ą.
Asad Saeeduddin
81

Tak, sprawdź dekorator staticmethod :

>>> class C:
...     @staticmethod
...     def hello():
...             print "Hello World"
...
>>> C.hello()
Hello World
Paolo Bergantino
źródło
54

Naprawdę nie musisz używać @staticmethoddekoratora. 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 ...

hichris123
źródło
Nie działałoby to w przypadku python 2.5.2 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ł)
Mahori
7
To nie działa w klasie. Python przejdzie selfjako pierwszy argument, chyba że powiesz mu, żeby tego nie robił. (patrz: dekorator)
Navin
9
W rzeczywistości jest to błędne w wersji 2.7 i zgodne z prawem od wersji 3.X (testowane poprawnie w wersji 3.2). Oznacza to, że nie można wywołać metody z kontekstu klasy bez dekoratora @staticmethod w wersji 2.7 i niższej. W 3.2 działa i odpowiednio wstawi selfref w zależności od tego, jak się nazywa. Przypadek testowy: pastebin.com/12DDV7DB .
spinkus
2
Technicznie dokładne, ale okropne rozwiązanie. staticmethodDekorator pozwala wywołać funkcję zarówno w klasie i instancji (rozwiązanie to nie powiedzie się podczas wywoływania funkcji w instancji).
Ethan Furman,
metodę można wywołać jak każdy inny atrybut obiektu za pomocą notacji kropkowej. class C: def callme(): print('called'); C.callme()
pouya
32

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 stringmodule 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 clsnie 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'])
Aaron Hall
źródło
11

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ą utilspakiet 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
Filip Dupanović
źródło
10

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 .

Matt Woelk
źródło
1

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 addjest wywoływana przez nazwę klasy, a Anie nazwę obiektu.

Aman Raheja
źródło
-3

Metody statyczne w języku Python można tworzyć na dwa sposoby.

  1. 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

  1. 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

Codemaker
źródło
Właśnie podałeś przykłady i podałeś sposoby, jak to zrobić. Nie wyjaśniłeś, co oznaczają te metody.
zixuan
-4

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.

użytkownik1928764
źródło
Nie odpowiedziałeś na pytanie, ale podałeś przykład.
zixuan