Статические методы в Python?

1721

Возможно ли иметь статические методы в Python, которые я мог бы вызывать без инициализации класса, например:

ClassName.static_method()
Джоан Венге
источник

Ответы:

2027

Да, используя статический метод декоратор

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: добавлен синтаксис функции декоратора.

DBR
источник
15
Зачем добавлять @staticmethodукрашения или использовать указатель на функцию, когда вы можете просто избежать первого selfпараметра? Ну, для объекта aвы не сможете вызывать a.your_static_method(), что разрешено на других языках, но это в любом случае считается плохой практикой, и компилятор всегда предупреждает об этом
SomethingSomething
1
Я использую Python 2.7.12, с декоратором @staticmethod я не могу вызвать первый статический метод, определенный. Его ошибка получения : TypeError
вызывается
Супратим Самантрай, ты уверен? потому что здесь четко указано, что его можно использовать после 2.4.
realslimshanky
11
@SomethingSomething, если вы не используете имя переменной «self», но не добавляете декоратор, первый аргумент, например, arg1, все равно будет ссылкой на экземпляр self. Само название - это всего лишь соглашение.
Джордж Мутсопулос
1
@ GeorgeMoutsopoulos - согласился в целом. Но - вы все равно сможете вызывать метод так ClassName.methodName(), как если бы он был статическим, и тогда selfметод не будет предоставлен. Как вы сказали, все еще можно будет также вызывать этот метод как ClassInstance.methodName()и selfбудет предоставлен в качестве первого параметра, независимо от его имени.
SomethingSomething
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декоратор. Просто объявите метод (не ожидающий параметра self) и вызовите его из класса. Декоратор существует только в том случае, если вы хотите иметь возможность вызывать его также из экземпляра (что не было тем, что вы хотели сделать)

В основном вы просто используете функции, хотя ...

hichris123
источник
Это не будет работать для 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: метод unbound static1 () должен быть вызван с пустым экземпляром в качестве первого аргумента (вместо этого ничего не получил)
Махори
7
Это не работает внутри класса. Python передаст selfв качестве первого аргумента, если вы не скажете, что нет. (см .: декоратор)
Navin
9
На самом деле это неверно в 2.7 и законно в 3.X (проверено в 3.2). То есть вы не можете вызывать метод из контекста класса без декоратора @staticmethod в 2.7 и ниже. В 3.2 он работает и вставит selfссылку соответствующим образом в зависимости от того, как он вызывается. Тестовый пример: pastebin.com/12DDV7DB .
spinkus
2
Технически точное, но ужасное решение. staticmethodДекоратор позволяет вызвать функцию как класс и экземпляр (это решение не выполняется при вызове функции на примере).
Итан Фурман
метод может быть вызван как любой другой атрибут объекта с использованием точечной нотации. class C: def callme(): print('called'); C.callme()
Pouya
32

Статические методы в Python?

Возможно ли иметь статические методы в Python, чтобы я мог вызывать их без инициализации класса, например:

ClassName.StaticMethod()

Да, статические методы могут быть созданы следующим образом (хотя для Pythonic немного более важно использовать подчеркивание вместо 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, что метод класса получает класс в качестве неявного первого аргумента, а в случае подкласса он получает подкласс в качестве неявного первого аргумента.

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

Codemaker
источник
Вы только что предоставили примеры и поделились, как вы можете это сделать. Вы не объяснили, что означают методы.
Цзысуань
-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 так или иначе действуют на комплексные числа.

user1928764
источник
Вы не ответили на вопрос, но вместо этого предоставили пример.
Цзысуань