В чем разница между @staticmethod и @classmethod?
В чем разница между функцией, украшенной @staticmethod
и один украшен @classmethod
21 ответ
Может быть, немного примера кода поможет: обратите внимание на разницу в сигнатурах вызовов foo
, class_foo
и static_foo
:
class A(object):
def foo(self,x):
print "executing foo(%s,%s)"%(self,x)
@classmethod
def class_foo(cls,x):
print "executing class_foo(%s,%s)"%(cls,x)
@staticmethod
def static_foo(x):
print "executing static_foo(%s)"%x
a=A()
Ниже приведен обычный способ, которым экземпляр объекта вызывает метод. Экземпляр объекта, a
, неявно передается в качестве первого аргумента.
a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>,1)
При использовании методов класса класс экземпляра объекта неявно передается в качестве первого аргумента вместо self
.
a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
Вы также можете вызвать class_foo
, используя класс. На самом деле, если вы определите что-то, чтобы быть
метод класса, это, вероятно, потому, что вы собираетесь вызывать его из класса, а не из экземпляра класса. A.foo(1)
вызвало бы ошибку TypeError, но A.class_foo(1)
работает просто отлично:
A.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
Один из способов использования методов класса - создание наследуемых альтернативных конструкторов .
При использовании статических методов ни self
(экземпляр объекта), ни cls
(класс) неявно передается в качестве первого аргумента. Они ведут себя как простые функции, за исключением того, что вы можете вызывать их из экземпляра или класса:
a.static_foo(1)
# executing static_foo(1)
A.static_foo('hi')
# executing static_foo(hi)
Статические методы используются для группировки функций, которые имеют некоторую логическую связь с классом для класса.
foo
- это просто функция, но когда вы вызываете a.foo
вы не просто получаете функцию,
вы получаете «частично примененную» версию функции с экземпляром объекта a
, связанным в качестве первого аргумента функции. foo
ожидает 2 аргумента, тогда как a.foo
ожидает 1 аргумент.
a
связан с foo
, Вот что подразумевается под термином «связанный» ниже:
print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>
С a.class_foo
, a
не привязан к class_foo
, а скорее к классу A
связан с class_foo
.
print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>
Здесь со статическим методом, даже если это метод, a.static_foo
просто возвращает
хорошая функция ole без привязки аргументов. static_foo
ожидает 1 аргумент и
a.static_foo
также ожидает 1 аргумент.
print(a.static_foo)
# <function static_foo at 0xb7d479cc>
И, конечно, то же самое происходит, когда вы вызываете static_foo
с классом A
.
print(A.static_foo)
# <function static_foo at 0xb7d479cc>
staticmethod - это метод, который ничего не знает о классе или экземпляре, для которого он был вызван. Он просто получает переданные аргументы, без первого неявного аргумента. Это в принципе бесполезно в Python - вы можете просто использовать функцию модуля вместо статического метода.
classmethod , с другой стороны, - это метод, передающий класс, к которому он был вызван, или класс экземпляра, к которому он был вызван, в качестве первого аргумента. Это полезно, когда вы хотите, чтобы метод был фабрикой для класса: поскольку он получает фактический класс, для которого он был вызван, в качестве первого аргумента, вы всегда можете создать экземпляр нужного класса, даже если задействованы подклассы. Посмотрите, например, как dict.fromkeys()
, метод класса, возвращает экземпляр подкласса при вызове в подклассе:
>>> class DictSubclass(dict):
... def __repr__(self):
... return "DictSubclass"
...
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>
Официальные документы по питону:
Метод класса получает класс как неявный первый аргумент, так же, как Метод экземпляра получает экземпляр. Чтобы объявить метод класса, используйте этот идиома:
class C: @classmethod def f(cls, arg1, arg2, ...): ...
Форма
@classmethod
является функцией декоратор - см. описание определения функций в функции определения для подробностей.Это может быть вызвано либо в классе (например,
C.f()
) или в экземпляре (например,C().f()
). Экземпляр игнорируется за исключением его класса. Если метод класса вызывается для производного класс, объект производного класса передан как подразумеваемый первый аргумент.Методы класса отличаются от C ++ или статические методы Java. Если ты хочешь см.
staticmethod()
в этом раздел.
Статический метод не получает неявный первый аргумент. Чтобы объявить статический метод, используйте эту идиому:
class C: @staticmethod def f(arg1, arg2, ...): ...
Форма
@staticmethod
является функцией декоратор - см. описание определения функций в функции определения для подробностей.Это может быть вызвано либо в классе (например,
C.f()
) или в экземпляре (например,C().f()
). Экземпляр игнорируется за исключением его класса.Статические методы в Python похожи к найденным в Java или C ++. Для более продвинутая концепция, см.
classmethod()
в этом разделе.
Здесь небольшая статья вопрос
Функция @staticmethod - это не что иное, как функция, определенная внутри класса. Он вызывается без создания экземпляра класса первым. Это определение является неизменным с помощью наследования.
Функция@classmethod также может вызываться без создания экземпляра класса, но ее определение следует за Sub-классом, а не родительским классом, через наследование. Это потому, что первым аргументом функции @classmethod всегда должен быть cls (class).
Чтобы решить, использовать ли @staticmethod или @classmethod . Если ваш метод обращается к другим переменным /методам в вашем классе, используйте @classmethod . С другой стороны, если ваш метод не касается каких-либо других частей класса, используйте @staticmethod.
class Apple:
_counter = 0
@staticmethod
def about_apple():
print('Apple is good for you.')
# note you can still access other member of the class
# but you have to use the class instance
# which is not very nice, because you have repeat yourself
#
# For example:
# @staticmethod
# print('Number of apples have been juiced: %s' % Apple._counter)
#
# @classmethod
# print('Number of apples have been juiced: %s' % cls._counter)
#
# @classmethod is especially useful when you move your function to other class,
# you don't have to rename the class reference
@classmethod
def make_apple_juice(cls, number_of_apples):
print('Make juice:')
for i in range(number_of_apples):
cls._juice_this(i)
@classmethod
def _juice_this(cls, apple):
print('Juicing %d...' % apple)
cls._counter += 1
В чем разница между @staticmethod и @classmethod в Python?
Возможно, вы видели код Python, подобный этому псевдокоду, который демонстрирует подписи различных типов методов и предоставляет строку документации для объяснения каждого из них:
class Foo(object):
def a_normal_instance_method(self, arg_1, kwarg_2=None):
'''
Return a value that is a function of the instance with its
attributes, and other arguments such as arg_1 and kwarg2
'''
@staticmethod
def a_static_method(arg_0):
'''
Return a value that is a function of arg_0. It does not know the
instance or class it is called from.
'''
@classmethod
def a_class_method(cls, arg1):
'''
Return a value that is a function of the class and other arguments.
respects subclassing, it is called with the class it is called from.
'''
Нормальный метод экземпляра
Сначала я объясню a_normal_instance_method
. Это точно называется « метод экземпляра ». Когда используется метод экземпляра, он используется как частичная функция (в отличие от функции total, определенной для всех значений при просмотре в исходном коде), то есть при использовании первый из аргументов предопределен как экземпляр объект со всеми заданными атрибутами. Он имеет экземпляр объекта, связанный с ним, и он должен вызываться из экземпляра объекта. Как правило, он будет обращаться к различным атрибутам экземпляра.
Например, это экземпляр строки:
', '
если мы используем метод экземпляра, join
в этой строке, чтобы присоединиться к другой итерации,
вполне очевидно, что это функция экземпляра, в дополнение к функции итеративного списка, ['a', 'b', 'c']
:
>>> ', '.join(['a', 'b', 'c'])
'a, b, c'
Связанные методы
Методы экземпляра можно связать с помощью точечного поиска для последующего использования.
Например, это связывает метод str.join
с ':'
экземпляр:
>>> join_with_colons = ':'.join
И позже мы можем использовать это как функцию, с которой уже связан первый аргумент. Таким образом, он работает как частичная функция в экземпляре:
>>> join_with_colons('abcde')
'a:b:c:d:e'
>>> join_with_colons(['FF', 'FF', 'FF', 'FF', 'FF', 'FF'])
'FF:FF:FF:FF:FF:FF'
Статический метод
Статический метод не принимает экземпляр в качестве аргумента.
Это очень похоже на функцию уровня модуля.
Однако функция уровня модуля должна находиться в модуле и специально импортироваться в другие места, где она используется.
Однако, если он присоединен к объекту, он будет удобно следовать за объектом через импорт и наследование.
Пример статического метода - str.maketrans
, перемещенный из string
модуль в Python 3. Он делает таблицу перевода пригодной для использования str.translate
. Это выглядит довольно глупо при использовании из экземпляра строки, как показано ниже, но импорт функции из модуля string
довольно неуклюжий и приятно иметь возможность вызывать его из класса, как в str.maketrans
# demonstrate same function whether called from instance or not:
>>> ', '.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
>>> str.maketrans('ABC', 'abc')
{65: 97, 66: 98, 67: 99}
В Python 2 вы должны импортировать эту функцию из все менее полезного строкового модуля:
>>> import string
>>> 'ABCDEFG'.translate(string.maketrans('ABC', 'abc'))
'abcDEFG'
Метод класса
Метод класса похож на метод экземпляра в том, что он принимает неявный первый аргумент, но вместо того, чтобы брать экземпляр, он принимает класс. Часто они используются в качестве альтернативных конструкторов для лучшего семантического использования и поддерживают наследование.
Наиболее каноническим примером встроенного метода класса является dict.fromkeys
. Он используется в качестве альтернативного конструктора dict (хорошо подходит для случаев, когда вы знаете, какие у вас ключи, и хотите получить для них значение по умолчанию.)
>>> dict.fromkeys(['a', 'b', 'c'])
{'c': None, 'b': None, 'a': None}
Когда мы создаем подкласс dict, мы можем использовать тот же конструктор, который создает экземпляр подкласса.
>>> class MyDict(dict): 'A dict subclass, use to demo classmethods'
>>> md = MyDict.fromkeys(['a', 'b', 'c'])
>>> md
{'a': None, 'c': None, 'b': None}
>>> type(md)
<class '__main__.MyDict'>
См. исходный код pandas для других похожих примеры альтернативных конструкторов, а также см. официальную документацию по Python по адресу classmethod
и staticmethod
@decorators были добавлены в python 2.4. Если вы используете python <2.4 вы можете использовать функции classmethod () и staticmethod ().
Например, если вы хотите создать фабричный метод (функция, возвращающая экземпляр другой реализации класса в зависимости от того, какой аргумент он получает), вы можете сделать что-то вроде:
class Cluster(object):
def _is_cluster_for(cls, name):
"""
see if this class is the cluster with this name
this is a classmethod
"""
return cls.__name__ == name
_is_cluster_for = classmethod(_is_cluster_for)
#static method
def getCluster(name):
"""
static factory method, should be in Cluster class
returns a cluster object for the given name
"""
for cls in Cluster.__subclasses__():
if cls._is_cluster_for(name):
return cls()
getCluster = staticmethod(getCluster)
Также обратите внимание, что это хороший пример использования метода класса и статического метода, Статический метод явно принадлежит классу, поскольку он использует класс Cluster для внутреннего использования. Метод класса нуждается только в информации о классе, а не в экземпляре объекта.
Еще одно преимущество создания метода _is_cluster_for
в качестве метода класса заключается в том, что подкласс может принять решение об изменении своей реализации, возможно потому, что он довольно общий и может обрабатывать более одного типа кластеров, поэтому просто проверить имя класса будет недостаточно.
Думаю, лучше задать вопрос: «Когда бы вы использовали @classmethod vs @staticmethod?»
@classmethod позволяет вам легко получить доступ к закрытым членам, связанным с определением класса. это отличный способ создавать синглтоны или фабричные классы, которые контролируют количество экземпляров созданных объектов.
@staticmethod обеспечивает предельный прирост производительности, но мне еще предстоит увидеть продуктивное использование статического метода в классе, который не может быть реализован как отдельная функция вне класса.
Статические методы:
- Простые функции без аргумента self.
- Работа с атрибутами класса; не в атрибутах экземпляра.
- Может вызываться как через класс, так и через экземпляр.
- Встроенная функция staticmethod () используется для их создания.
Преимущества статических методов:
- Локализует имя функции в классе
- Он перемещает код функции ближе к месту его использования
-
Удобнее импортировать функции по сравнению с функциями уровня модуля, поскольку каждый метод не нужно специально импортировать.
@staticmethod def some_static_method(*args, **kwds): pass
Методы класса:
- Функции с первым аргументом в качестве имени класса.
- Может вызываться как через класс, так и через экземпляр.
-
Они создаются с помощью встроенной функции classmethod.
@classmethod def some_class_method(cls, *args, **kwds): pass
@staticmethod
просто отключает функцию по умолчанию как дескриптор метода. classmethod оборачивает вашу функцию в вызываемый контейнер, который передает ссылку на собственный класс в качестве первого аргумента:
>>> class C(object):
... pass
...
>>> def f():
... pass
...
>>> staticmethod(f).__get__(None, C)
<function f at 0x5c1cf0>
>>> classmethod(f).__get__(None, C)
<bound method type.f of <class '__main__.C'>>
На самом деле, classmethod
накладные расходы времени выполнения, но позволяют получить доступ к классу-владельцу. В качестве альтернативы я рекомендую использовать метакласс и поместить методы класса в этот метакласс:
>>> class CMeta(type):
... def foo(cls):
... print cls
...
>>> class C(object):
... __metaclass__ = CMeta
...
>>> C.foo()
<class '__main__.C'>
Полное руководство по использованию статических классов или абстрактные методы в Python - одна из хороших ссылок на эту тему, и кратко изложите ее следующим образом.
Функция @staticmethod
- это не что иное, как функция, определенная внутри класса. Он вызывается без создания экземпляра класса первым. Это определение является неизменным с помощью наследования.
- Python не обязан создавать экземпляр метода для объекта.
- Это облегчает читабельность кода и не зависит от состояния самого объекта;
@classmethod
также может вызываться без создания экземпляра класса, но ее определение следует подклассу, а не классу Parent. через наследование может быть переопределено подклассом. Это потому, что первый аргумент для функции @classmethod
всегда должен быть cls (class).
- Фабричные методы , которые используются для создания экземпляра класса, используя, например, некоторую предварительную обработку.
- Статические методы, вызывающие статические методы : если вы разбили статические методы на несколько статических методов, вам не следует жестко кодировать имя класса, а использовать методы класса
Я начал изучать язык программирования на C ++, а затем на Java, а затем на Python, поэтому этот вопрос меня тоже очень беспокоил, пока я не понял простое использование каждого из них.
Метод класса: Python, в отличие от Java и C ++, не перегружен конструктором. И поэтому для достижения этого вы можете использовать classmethod
. Следующий пример объяснит это
Давайте рассмотрим, что у нас есть класс Person
, который принимает два аргумента first_name
и last_name
и создает экземпляр Person.
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
Теперь, если требование возникает там, где вам нужно создать класс, используя только одно имя, просто first_name
, вы не могу сделать что-то подобное в python.
Это приведет к ошибке при попытке создать объект (экземпляр).
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def __init__(self, first_name):
self.first_name = first_name
Однако вы можете достичь того же, используя @classmethod
, как указано ниже
class Person(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@classmethod
def get_person(cls, first_name):
return cls(first_name, "")
Статический метод : это довольно просто, он не привязан к экземпляру или классу, и вы можете просто вызвать его, используя имя класса.
Итак, скажем, в приведенном выше примере вам нужна проверка, что first_name
не должен превышать 20 символов, вы можете просто сделать это.
@staticmethod
def validate_name(name):
return len(name) <= 20
и вы можете просто позвонить, используя Имя класса
Person.validate_name("Gaurang Shah")
Еще одно соображение, касающееся статического метода и классового метода, связано с наследованием. Скажем, у вас есть следующий класс:
class Foo(object):
@staticmethod
def bar():
return "In Foo"
И затем вы хотите переопределить bar()
в дочернем классе:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
Это работает, но обратите внимание, что теперь реализация bar()
в дочернем классе (Foo2
) больше не может использовать что-то специфическое для этого класса. Например, скажем, у Foo2
был метод с именем magic()
, который вы хотите использовать в Foo2
реализации bar()
:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
@staticmethod
def magic():
return "Something useful you'd like to use in bar, but now can't"
Обходной путь здесь будет заключаться в вызове Foo2.magic()
в bar()
, но затем вы повторяете себя (если имя Foo2
изменится, вы должны будете не забыть обновить этот метод bar()
).
Для меня это небольшое нарушение принципа открытия /закрытия , поскольку решение, принятое в Foo
, влияет на вашу способность реорганизовывать общий код в производном классе (т. е. он менее открыт для расширения). Если бы bar()
было бы classmethod
, мы бы быть в порядке:
class Foo(object):
@classmethod
def bar(cls):
return "In Foo"
class Foo2(Foo):
@classmethod
def bar(cls):
return "In Foo2 " + cls.magic()
@classmethod
def magic(cls):
return "MAGIC"
print Foo2().bar()
Дает: In Foo2 MAGIC
Позвольте мне сначала рассказать о сходстве метода, украшенного @classmethod vs @staticmethod.
Сходство . Их обоих можно вызывать для самого класса , а не только для экземпляра класса. Итак, оба они в каком-то смысле являются методами класса .
Разница . Метод класса получит сам класс в качестве первого аргумента, а метод статики - нет.
Таким образом, статический метод, в некотором смысле, не связан с самим классом и просто висит там только потому, что он может иметь связанную функциональность.
>>> class Klaus:
@classmethod
def classmthd(*args):
return args
@staticmethod
def staticmthd(*args):
return args
# 1. Call classmethod without any arg
>>> Klaus.classmthd()
(__main__.Klaus,) # the class gets passed as the first argument
# 2. Call classmethod with 1 arg
>>> Klaus.classmthd('chumma')
(__main__.Klaus, 'chumma')
# 3. Call staticmethod without any arg
>>> Klaus.staticmthd()
()
# 4. Call staticmethod with 1 arg
>>> Klaus.staticmthd('chumma')
('chumma',)
Я попытаюсь объяснить основные различия на примере.
class A(object):
x = 0
def say_hi(self):
pass
@staticmethod
def say_hi_static():
pass
@classmethod
def say_hi_class(cls):
pass
def run_self(self):
self.x += 1
print self.x # outputs 1
self.say_hi()
self.say_hi_static()
self.say_hi_class()
@staticmethod
def run_static():
print A.x # outputs 0
# A.say_hi() # wrong
A.say_hi_static()
A.say_hi_class()
@classmethod
def run_class(cls):
print cls.x # outputs 0
# cls.say_hi() # wrong
cls.say_hi_static()
cls.say_hi_class()
1 - мы можем напрямую вызывать методы static и classmethods без инициализации
# A.run_self() # wrong
A.run_static()
A.run_class()
2 - Статический метод не может вызывать метод self, но может вызывать другой статический метод и метод класса
3 - Статический метод принадлежит классу и не будет использовать объект вообще.
4 - Метод класса связан не с объектом, а с классом.
@classmethod: может использоваться для создания общего глобального доступа ко всем экземплярам, созданным в этом классе ..... например, при обновлении записи несколькими пользователями .... В частности, я обнаружил, что это полезно при создании синглетонов ..:)
@static method: не имеет ничего общего с классом или экземпляром, связанным с ... но для удобства чтения можно использовать статический метод
Мой вклад демонстрирует разницу между @classmethod
, @staticmethod
и методы экземпляра, включая то, как экземпляр может косвенно вызывать @staticmethod
. Но вместо того, чтобы косвенно вызывать @staticmethod
из экземпляра, сделать его частным может быть более «питонным». Получение чего-то из закрытого метода здесь не продемонстрировано, но в основном это та же концепция.
#!python3
from os import system
system('cls')
# % % % % % % % % % % % % % % % % % % % %
class DemoClass(object):
# instance methods need a class instance and
# can access the instance through 'self'
def instance_method_1(self):
return 'called from inside the instance_method_1()'
def instance_method_2(self):
# an instance outside the class indirectly calls the static_method
return self.static_method() + ' via instance_method_2()'
# class methods don't need a class instance, they can't access the
# instance (self) but they have access to the class itself via 'cls'
@classmethod
def class_method(cls):
return 'called from inside the class_method()'
# static methods don't have access to 'cls' or 'self', they work like
# regular functions but belong to the class' namespace
@staticmethod
def static_method():
return 'called from inside the static_method()'
# % % % % % % % % % % % % % % % % % % % %
# works even if the class hasn't been instantiated
print(DemoClass.class_method() + '\n')
''' called from inside the class_method() '''
# works even if the class hasn't been instantiated
print(DemoClass.static_method() + '\n')
''' called from inside the static_method() '''
# % % % % % % % % % % % % % % % % % % % %
# >>>>> all methods types can be called on a class instance <<<<<
# instantiate the class
democlassObj = DemoClass()
# call instance_method_1()
print(democlassObj.instance_method_1() + '\n')
''' called from inside the instance_method_1() '''
# # indirectly call static_method through instance_method_2(), there's really no use
# for this since a @staticmethod can be called whether the class has been
# instantiated or not
print(democlassObj.instance_method_2() + '\n')
''' called from inside the static_method() via instance_method_2() '''
# call class_method()
print(democlassObj.class_method() + '\n')
''' called from inside the class_method() '''
# call static_method()
print(democlassObj.static_method())
''' called from inside the static_method() '''
"""
# whether the class is instantiated or not, this doesn't work
print(DemoClass.instance_method_1() + '\n')
'''
TypeError: TypeError: unbound method instancemethod() must be called with
DemoClass instance as first argument (got nothing instead)
'''
"""
Методы класса, как следует из названия, используются для внесения изменений в классы, а не в объекты. Чтобы внести изменения в классы, они изменят атрибуты класса (не атрибуты объекта), поскольку именно так вы обновляете классы. По этой причине методы класса принимают класс (условно обозначаемый как «cls») в качестве первого аргумента.
class A(object):
m=54
@classmethod
def class_method(cls):
print "m is %d" % cls.m
Статические методы, с другой стороны, используются для выполнения функций, которые не связаны с классом, т.е. они не будут читать или записывать переменные класса. Следовательно, статические методы не принимают классы в качестве аргументов. Они используются для того, чтобы классы могли выполнять функции, которые не связаны напрямую с назначением класса.
class X(object):
m=54 #will not be referenced
@staticmethod
def static_method():
print "Referencing/calling a variable or function outside this class. E.g. Some global variable/function."
Анализируйте @staticmethod буквально , предоставляя различные идеи.
Обычный метод класса - это неявный динамический метод, который принимает экземпляр в качестве первого аргумента.
Напротив, статический метод не принимает экземпляр в качестве первого аргумента, поэтому он называется «статическим» .
Статический метод действительно является такой же нормальной функцией, как и функции вне определения класса.
К счастью, он сгруппирован в класс только для того, чтобы стоять ближе к месту его применения, или вы можете прокрутить его, чтобы найти его.
#!/usr/bin/python
#coding:utf-8
class Demo(object):
def __init__(self,x):
self.x = x
@classmethod
def addone(self, x):
return x+1
@staticmethod
def addtwo(x):
return x+2
def addthree(self, x):
return x+3
def main():
print Demo.addone(2)
print Demo.addtwo(2)
#print Demo.addthree(2) #Error
demo = Demo(2)
print demo.addthree(2)
if __name__ == '__main__':
main()
Быстрый взлом других идентичных методов в iPython показывает, что @staticmethod
дает незначительный прирост производительности (в наносекундах), но в противном случае это кажется, не выполняет никакой функции. Кроме того, любое повышение производительности, вероятно, будет устранено дополнительной работой по обработке метода с помощью staticmethod()
во время компиляции (что происходит до любого кода выполнение при запуске сценария).
Ради читабельности кода я бы избегал @staticmethod
, если только ваш метод не будет использоваться для работы, когда подсчет наносекунд считается .