Статичні методи в Python?


1719

Чи можливі статичні методи в Python, які я міг би викликати без ініціалізації класу, наприклад:

ClassName.static_method()

Відповіді:


2026

Так, використовуючи декоратор статичного методу

class MyClass(object):
    @staticmethod
    def the_static_method(x):
        print(x)

MyClass.the_static_method(2)  # outputs 2

Зауважте, що деякий код може використовувати старий метод визначення статичного методу, використовуючи staticmethodяк функцію, а не декоратор. Це слід використовувати лише в тому випадку, якщо вам потрібно підтримувати старовинні версії Python (2.2 і 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

Це повністю ідентично першому прикладу (використовуючи @staticmethod), тільки не використовуючи синтаксис симпатичного декоратора

Нарешті, використовуйте staticmethod()економно! Є дуже мало ситуацій, коли статичні методи необхідні в Python, і я бачив їх використання багато разів, коли окрема функція "верхнього рівня" була б зрозумілішою.


З документації подано дослівно :

Статичний метод не отримує неявного першого аргументу. Щоб оголосити статичний метод, використовуйте цю ідіому:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

Форма @staticmethod є декоратором функцій - детально див. Опис визначень функцій у визначеннях функції .

Його можна викликати або на класі (наприклад C.f()), або на екземплярі (наприклад, C().f()). Екземпляр ігнорується за винятком класу.

Статичні методи в Python схожі на ті, які знайдені в Java або C ++. Більш досконалу концепцію див classmethod().

Для отримання додаткової інформації про статичні методи зверніться до документації щодо ієрархії стандартного типу в Ієрархії стандартного типу .

Нове у версії 2.2.

Змінено у версії 2.4: додано синтаксис декоратора функції.


15
Навіщо додавати @staticmethodдекор або використовувати покажчик функції, коли ви можете просто уникнути першого selfпараметра? Що ж, для об'єкта aви не зможете зателефонувати a.your_static_method(), що дозволено іншими мовами, але це все одно вважається поганою практикою, і компілятор завжди попереджає про це
SomethingSomething

1
Я використовую python 2.7.12, з декоратором @staticmethod я не можу викликати перший визначений статичний метод. Його помилка givin
викликати

Супратім Саманрай, ви впевнені? оскільки тут чітко сказано, що його можна використовувати після 2.4.
realslimshanky

11
@SomethingSomething, якщо ви не використовуєте назву змінної "self", але не додаєте декоратор, першим аргументом, наприклад arg1, як і раніше буде посилання на власний екземпляр. Ім'я "Я" - це лише умова.
Джордж Муцопулос

1
@GeorgeMoutsopoulos - загальна згода. Але - ви все одно зможете назвати метод так ClassName.methodName(), як ніби він був статичним, і тоді selfметод не буде наданий. Як ви вже сказали, цей метод все ще можна буде називати як ClassInstance.methodName(), і selfвін буде надаватися як перший параметр, незалежно від його назви.
Щось щось щось

220

Я думаю, що Стівен насправді має рацію . Щоб відповісти на вихідне запитання, тоді, щоб встановити метод класу, просто припустіть, що перший аргумент не буде викликом екземпляра, а потім переконайтесь, що ви викликаєте метод лише з класу.

(Зверніть увагу, що ця відповідь стосується Python 3.x. У Python 2.x ви отримаєте TypeErrorдля виклику методу в самому класі.)

Наприклад:

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)

У цьому коді метод "rollCall" передбачає, що перший аргумент не є екземпляром (як це було б, якби його викликав екземпляр замість класу). Поки "rollCall" викликається з класу, а не екземпляра, код буде працювати нормально. Якщо ми спробуємо викликати "rollCall" з екземпляра, наприклад:

rex.rollCall(-1)

однак, це призведе до виникнення винятку, оскільки він надішле два аргументи: себе та -1, а "rollCall" визначено лише для прийняття одного аргументу.

До речі, rex.rollCall () надішле правильну кількість аргументів, але також спричинить виняток, оскільки тепер n буде представляти екземпляр Dog (тобто, rex), коли функція очікує, що n буде числовим.

Ось де виходить прикраса: Якщо ми передуємо методу "rollCall"

@staticmethod

потім, прямо заявляючи, що метод є статичним, ми можемо навіть викликати його з екземпляра. Тепер,

rex.rollCall(-1)

працювали б. Вставлення @staticmethod перед визначенням методу, тоді, зупиняє екземпляр відсилати себе як аргумент.

Ви можете перевірити це, спробувавши наступний код із коментованим рядком @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)

8
Перший приклад (дзвінок по класу без @staticmethod) не працює для мене на Python 2.7. Я отримую "TypeError: метод незв'язаного методу rollCall () повинен бути викликаний з екземпляром Dog як перший аргумент (замість нього отриманий int екземпляр)"
tba

2
Це не працює. І Dog.rollCall (-1), і rex.rollCall (-1) повертаються однаковоTypeError: unbound method rollCall() must be called with Dog instance as first argument (got int instance instead)
Mittenchops

3
@MestreLion Я думаю, що це не є величезною перевагою, оскільки все, що він робить, - це заплутати статичні та екземплярні методи і зробити один схожим на інший. Якщо ви знаєте, що метод є статичним, вам слід отримати доступ до нього як статичний метод. Те, що метод не потребує або не використовує ваш примірник, повинно бути зрозумілим, де б ви не використовували цей метод. Я завжди використовую T.my_static_method()або type(my_t_instance).my_static_method(), оскільки це ясніше і відразу стає очевидним, що я викликаю статичний метод.
Асад Саєдюддін


54

Вам не потрібно використовувати @staticmethodдекоратор. Просто оголосивши метод (який не очікує параметру самоврядування) і викликати його з класу. Декоратор є лише в тому випадку, якщо ви хочете мати можливість викликати його і з екземпляра (що було не тим, що ви хотіли зробити)

В основному, ви просто використовуєте функції, хоча ...


Це не буде працювати для python 2.5.2 class Dummy: def static1(): print "hello from static1" @staticmethod def static2(): print "hello from static2" Dummy.static2() Dummy.static1() Вихід: привіт від static2 Traceback <останній останній виклик>: Файл "ll.py", рядок 46, в <module> Dummy.static1 () TypeError: метод без зв’язку static1 () повинен бути зателефонував разом із екземпляром манекена як перший аргумент (замість цього нічого не отримали)
Магорі

7
Це не працює всередині класу. Python передасть selfяк перший аргумент, якщо ви не скажете цього. (див .: декоратор)
Навін

9
Насправді це неправильно в 2.7 та законно станом на 3.Х (перевірено штрафом у 3.2). Тобто ви не можете викликати метод з контексту класу без декоратора @staticmethod в 2.7 і нижче. У 3.2 він працює і додасть selfпосилання відповідно до того, як його називають. Тестовий випадок: pastebin.com/12DDV7DB .
шпигун

2
Технічно точне, але жахливе рішення. staticmethodДекоратор дозволяє викликати функцію як клас і екземпляр (це рішення не виконується при виконанні функції на прикладі).
Етан Фурман

метод може бути викликаний, як і будь-який інший атрибут об'єкта, використовуючи крапкові позначення. class C: def callme(): print('called'); C.callme()
pouya

32

Статичні методи в Python?

Чи можливі статичні методи в Python, щоб я міг викликати їх без ініціалізації класу, наприклад:

ClassName.StaticMethod()

Так, статичні методи можуть бути створені таким чином (хоча для Pytonic трохи кращим є використання підкреслення замість CamelCase для методів):

class ClassName(object):

    @staticmethod
    def static_method(kwarg1=None):
        '''return a value that is a function of kwarg1'''

У наведеному вище використовується синтаксис декоратора. Цей синтаксис еквівалентний

class ClassName(object):

    def static_method(kwarg1=None):
        '''return a value that is a function of kwarg1'''

    static_method = staticmethod(static_method)

Це можна використовувати так само, як ви описали:

ClassName.static_method()

Приклад вбудованого статичного методу - str.maketrans()в Python 3, який був функцією stringмодуля в Python 2.


Іншим варіантом, який можна використовувати під час опису, є classmethodрізниця в тому, що classmethod отримує клас у якості неявного першого аргументу, а якщо підклас - то він отримує підклас як неявний перший аргумент.

class ClassName(object):

    @classmethod
    def class_method(cls, kwarg1=None):
        '''return a value that is a function of the class and kwarg1'''

Зверніть увагу, що clsце не обов'язкове ім'я для першого аргументу, але більшість досвідчених кодерів Python вважають це погано зробленим, якщо ви використовуєте що-небудь інше.

Зазвичай вони використовуються як альтернативні конструктори.

new_instance = ClassName.class_method()

Прикладом вбудованого є dict.fromkeys():

new_dict = dict.fromkeys(['key1', 'key2'])

11

Крім особливостей того, як поводяться об'єкти статичного методу , існує певна краса, яку ви можете вразити ними, якщо мова йде про організацію коду на рівні модуля.

# 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

Тепер це стає трохи більш інтуїтивно зрозумілим і самодокументованим, в якому контексті мають бути використані певні компоненти, і це ідеально підходить для іменування різних тестових випадків, а також прямого підходу до того, як тестові модулі відображають фактичні модулі під тести для пуристів .

Мені часто здається можливим застосувати такий підхід до організації корисного коду проекту. Досить часто люди одразу поспішають і створюють utilsпакет і в кінцевому підсумку складають 9 модулів, з яких один має 120 LOC, а решта - два десятки LOC у кращому випадку. Я вважаю за краще почати з цього і перетворити його в пакет і створити модулі тільки для звірів, які справді їх заслуговують:

# 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

10

Мабуть, найпростіший варіант - просто поставити ці функції поза класом:

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"

За допомогою цього методу функції, що змінюють або використовують стан внутрішнього об'єкта (мають побічні ефекти), можуть зберігатися у класі, а функції корисних програм для багаторазового використання можуть бути переміщені назовні.

Скажімо, цей файл називається dogs.py. Щоб їх використовувати, ви б зателефонували dogs.barking_sound()замість dogs.Dog.barking_sound.

Якщо вам дійсно потрібен статичний метод, щоб бути частиною класу, ви можете використовувати декоратор staticmethod .


1

Отже, статичні методи - це методи, які можна назвати без створення об’єкта класу. Наприклад :-

    @staticmethod
    def add(a, b):
        return a + b

b = A.add(12,12)
print b

У наведеному вище прикладі метод addвикликається назвою класу, а Aне ім'ям об'єкта.


-3

Статичні методи Python можна створити двома способами.

  1. Використання 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))

Вихід:

Результат: 25

  1. Використання @staticmethod

    class Arithmetic:
    
    # create add static method
    @staticmethod
    def add(x, y):
        return x + y
    
    print('Result:', Arithmetic.add(15, 10))

Вихід:

Результат: 25


Ви просто навели приклади та поділилися способами, як це зробити. Ви не пояснили, що означають методи.
zixuan

-4

Я стикаюся з цим питанням час від часу. Випадок використання та приклад, який мені подобається:

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']
>>> 

Немає сенсу створювати об’єкт класу cmath, оскільки в об'єкті cmath немає стану. Однак cmath - це сукупність методів, які всі так чи інакше пов'язані. У моєму прикладі вище всі функції в cmath певним чином діють на складні числа.


Ви не відповіли на запитання, але натомість навели приклад.
zixuan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.