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

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

ClassName.StaticMethod ( )

9 ответов


да, с помощью staticmethod оформителя

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), просто не используя синтаксис nice decorator

и, наконец, использовать staticmethod() экономно! Есть очень мало ситуаций, когда статические методы необходимы в Python, и я видел их много раз, когда отдельная функция "верхнего уровня" была бы более ясной.


следующее дословно из документации::

статический метод не получает неявный первый аргумент. Чтобы объявить статический метод, используйте эту идиому:

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

форма @staticmethod функция оформителя - см. Описание определений функций в определения функции для сведения.

его можно вызвать либо в классе (например,C.f()) или на экземпляре (например,C().f()). Экземпляр игнорируется, за исключением его класса.

статические методы в Python аналогичны методам, найденным в Java или c++. Более продвинутую концепцию смотрите в разделе classmethod().

для получения дополнительной информации о статических методах обратитесь к документации по стандартной иерархии типов в стандартная иерархия типа.

новое в версии 2.2.

изменено в версии 2.4: добавлен синтаксис декоратора функций.


думаю, что Стивен на самом деле прав. Чтобы ответить на исходный вопрос, чтобы настроить метод класса, просто предположите, что первый аргумент не будет вызывающим экземпляром, а затем убедитесь, что вы вызываете только метод из класса.

(обратите внимание, что этот ответ относится к Python 3.х. В 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" определен только для принятия одного аргумент.

кстати, Рекс.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)

да, проверьте staticmethod оформитель:

>>> class C:
...     @staticmethod
...     def hello():
...             print "Hello World"
...
>>> C.hello()
Hello World

вы действительно не нужно использовать @staticmethod оформителя. Просто объявив метод (который не ожидает параметра self) и вызовите его из класса. Декоратор существует только в том случае, если вы хотите иметь возможность вызвать его из экземпляра (что было не то, что вы хотели сделать)

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


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

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

ClassName.StaticMethod()

да, статические методы могут быть созданы так (хотя это немного больше весть использовать подчеркивания вместо 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'])

Помимо особенностей того, как объекты статического метода вести себя, есть определенный вид красоты вы можете поразить с ними, когда дело доходит до организации кода уровня модуля.

# 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

возможно, самый простой вариант - просто вывести эти функции за пределы класса:

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 оформителя.


В Python 3:

вот еще один хороший способ использовать нестатические методы, поскольку они были статическими методами.

class Account:
# here is the main class

# a non-static method
def login(self, url, email, password):
    # as a convention self is passed as a 
    # placeholder for the class Object itself

    self.login_url = url
    self.user_email = email
    self.user_password = password

    print(login_url, user_email, user_password)

вызовите нестатический метод как static

"""
* Referencing the Account.login(self, url, email, password)
* Just call the `Account` object as the self 
"""
Account.login(Account, "https://example.com/login", "email@example.com", "password_example")

:$ https://example.com/login email@example.com password_example

я сталкиваюсь с этим вопросом время от времени. Случай использования и пример, который я люблю:

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 каким-то образом действуют на комплексные числа.