В чем разница между функцией, украшенной @staticmethod
и одной, украшенной @classmethod
?
В чем разница между функцией, украшенной @staticmethod
и одной, украшенной @classmethod
?
Ответы:
Может быть, пример кода поможет: обратите внимание на разницу в сигнатурах вызовов 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
может помочь организовать ваш код, переопределяясь подклассами. Без этого у вас были бы варианты функции, плавающей в пространстве имен модуля.
@staticmethod
- вы можете использовать его для удаления Cruft. Я реализую язык программирования на Python - библиотечные функции используют статический execute
метод, где пользовательские функции требуют аргументов экземпляра (т. Е. Тела функции). Этот декоратор устраняет предупреждения «неиспользуемый параметр self» в инспекторе PyCharm.
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
>>>
По сути, @classmethod
метод, первым аргументом которого является класс, из которого он вызывается (а не экземпляр класса), @staticmethod
не имеет никаких неявных аргументов.
Официальные документы по питону:
Метод класса получает класс как неявный первый аргумент, так же, как метод экземпляра получает экземпляр. Чтобы объявить метод класса, используйте эту идиому:
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 class, а не Parent, посредством наследования. Это потому, что первым аргументом функции @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
cls._counter
все равно будет, cls._counter
даже если код помещен в другой класс или имя класса изменено. Apple._counter
специфичен для Apple
класса; для другого класса или при изменении имени класса вам нужно будет изменить ссылочный класс.
В чем разница между @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
.
Я начал изучать язык программирования на 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
Теперь, если требование возникает там, где вам нужно создать класс, используя только одно имя, просто a 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
и вы могли бы просто позвонить с помощью class name
Person.validate_name("Gaurang Shah")
def __init__(self, first_name, last_name="")
вместо метода класса get_person
. Также результат будет точно таким же в этом случае.
Я думаю, что лучший вопрос: "Когда бы вы использовали @classmethod против @staticmethod?"
@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
метода в метод класса - это то, что подкласс может решить изменить свою реализацию, возможно, потому, что он довольно универсален и может обрабатывать более одного типа кластера, поэтому простой проверки имени класса будет недостаточно.
Статические методы:
Преимущества статических методов:
Удобнее импортировать функции по сравнению с функциями уровня модуля, поскольку каждый метод не нужно специально импортировать
@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'>
c = C(); c.foo()
поднимает AttributeError, вам придется сделать type(c).foo()
. Это также может считаться особенностью - хотя я не могу понять, почему вы захотите.
Полное руководство по использованию статических, классовых или абстрактных методов в Python - одна из хороших ссылок на эту тему, и подытожьте ее следующим образом.
@staticmethod
Функция - это не что иное, как функция, определенная внутри класса. Он вызывается без создания экземпляра класса первым. Это определение является неизменным через наследование.
@classmethod
Функция также может вызываться без создания экземпляра класса, но ее определение следует за Подклассом, а не Родительский класс посредством наследования может быть переопределен подклассом. Это потому, что первый аргумент для @classmethod
функции всегда должен быть cls (class).
Отличается только первый аргумент :
Более детально...
Когда вызывается метод объекта, ему автоматически присваивается дополнительный аргумент в self
качестве первого аргумента. То есть метод
def f(self, x, y)
должен быть вызван с 2 аргументами. self
автоматически передается, и это сам объект .
Когда метод оформлен
@classmethod
def f(cls, x, y)
автоматически предоставленный аргумент не self
, но класс self
.
Когда метод оформлен
@staticmethod
def f(x, y)
метод не имеет никакого автоматического аргумента вообще. Дается только параметры, с которыми он вызывается.
classmethod
в основном используется для альтернативных конструкторов. staticmethod
не использует состояние объекта. Это может быть функция, внешняя по отношению к классу. Он помещается только внутри класса для группировки функций с похожей функциональностью (например, как Math
статические методы класса Java )class Point
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def frompolar(cls, radius, angle):
"""The `cls` argument is the `Point` class itself"""
return cls(radius * cos(angle), radius * sin(angle))
@staticmethod
def angle(x, y):
"""this could be outside the class, but we put it here
just because we think it is logically related to the class."""
return atan(y, x)
p1 = Point(3, 2)
p2 = Point.frompolar(3, pi/4)
angle = Point.angle(3, 2)
Позвольте мне сначала рассказать о сходстве метода, украшенного @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 Foo(object):
@staticmethod
def bar():
return "In Foo"
И затем вы хотите переопределить bar()
в дочернем классе:
class Foo2(Foo):
@staticmethod
def bar():
return "In Foo2"
Это работает, но обратите внимание, что теперь bar()
реализация в дочернем классе ( Foo2
) больше не может использовать в своих интересах что-то определенное для этого класса. Например, у say 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
Я попытаюсь объяснить основную разницу на примере.
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: не имеет ничего общего с классом или экземпляром, связанным с ... но для удобства чтения можно использовать статический метод
Вы можете рассмотреть разницу между:
Class A:
def foo(): # no self parameter, no decorator
pass
а также
Class B:
@staticmethod
def foo(): # no self parameter
pass
Это изменилось между python2 и python3:
python2:
>>> A.foo()
TypeError
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
python3:
>>> A.foo()
>>> A().foo()
TypeError
>>> B.foo()
>>> B().foo()
Поэтому использование @staticmethod
для методов, вызываемых только из класса, стало необязательным в python3. Если вы хотите вызывать их как из класса, так и из экземпляра, вам все равно нужно использовать @staticmethod
декоратор.
Другие случаи были хорошо охвачены ответом unutbus.
Метод класса получает класс как неявный первый аргумент, так же, как метод экземпляра получает экземпляр. Это метод, который привязан к классу, а не к объекту класса. Он имеет доступ к состоянию класса, так как принимает параметр класса, который указывает на класс, а не на экземпляр объекта. Он может изменять состояние класса, которое будет применяться ко всем экземплярам класса. Например, он может изменить переменную класса, которая будет применима ко всем экземплярам.
С другой стороны, статический метод не получает неявный первый аргумент по сравнению с методами класса или методами экземпляра. И не может получить доступ или изменить состояние класса. Он принадлежит только классу, потому что с точки зрения дизайна это правильный путь. Но с точки зрения функциональности не связан, во время выполнения, с классом.
в качестве руководства используйте статические методы в качестве утилит, используйте методы класса, например, как фабрику. Или, может быть, определить синглтон. И используйте методы экземпляров для моделирования состояния и поведения экземпляров.
Надеюсь, мне было ясно!
Мой вклад демонстрирует разницу среди @classmethod
, @staticmethod
и методов экземпляра, в том числе , как экземпляр может косвенно называть @staticmethod
. Но вместо того, чтобы косвенно вызывать a @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 буквально предоставляя различные идеи.
Обычный метод класса - это неявный динамический метод, который принимает экземпляр в качестве первого аргумента.
Напротив, метод static не принимает экземпляр в качестве первого аргумента, поэтому он называется «статическим» .
Статический метод действительно является такой же нормальной функцией, как и те, которые находятся вне определения класса.
Он, к счастью, сгруппирован в класс только для того, чтобы стоять ближе там, где он применяется, или вы можете прокрутить круг, чтобы найти его.
Я думаю, что дать чисто Python-версию staticmethod
иclassmethod
поможет понять разницу между ними на уровне языка.
Оба они являются дескрипторами, не относящимися к данным (было бы легче понять их, если вы сначала знакомы с дескрипторами ).
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
return self.f
class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, cls=None):
def inner(*args, **kwargs):
if cls is None:
cls = type(obj)
return self.f(cls, *args, **kwargs)
return inner
staticmethod не имеет доступа к объектам объекта, класса или родительских классов в иерархии наследования. Он может быть вызван непосредственно в классе (без создания объекта).
classmethod не имеет доступа к атрибутам объекта. Однако он может обращаться к атрибутам класса и родительских классов в иерархии наследования. Он может быть вызван непосредственно в классе (без создания объекта). Если вызывается в объекте, то это то же самое, что и обычный метод, который не self.<attribute(s)>
обращается и не обращается кself.__class__.<attribute(s)>
только .
Думаю, у нас есть класс b=2
, мы создадим объект и заново установим его b=4
в нем. Статический метод не может получить доступ ни к чему из предыдущего. Classmethod может получить доступ .b==2
только через cls.b
. Нормальный метод может получить доступ как: .b==4
через, так self.b
и .b==2
через self.__class__.b
.
Мы могли бы следовать стилю KISS (пусть он будет простым, глупым): не используйте статические методы и методы классов, не используйте классы без их создания, обращайтесь только к атрибутам объекта self.attribute(s)
. Есть языки, где ООП реализован таким образом, и я думаю, что это неплохая идея. :)
Быстрый взлом других идентичных методов в iPython показывает, что @staticmethod
дает незначительный прирост производительности (в наносекундах), но в остальном он, похоже, не работает. Кроме того, любое повышение производительности, вероятно, будет устранено дополнительной работой по обработке метода staticmethod()
во время компиляции (что происходит до выполнения любого кода при запуске сценария).
Ради читабельности кода я бы избегал, @staticmethod
если ваш метод не будет использоваться для нагрузок, где подсчитываются наносекунды.