Яка різниця між функцією, прикрашеною @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)
За допомогою classmethods клас об'єктного примірника неявно передається як перший аргумент замість self
.
a.class_foo(1)
# executing class_foo(<class '__main__.A'>,1)
Ви також можете зателефонувати class_foo
за допомогою класу. Насправді, якщо ви визначаєте щось як метод class, це, мабуть, тому, що ви маєте намір викликати це з класу, а не з екземпляра класу. 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
- ви можете використовувати його для видалення крихти. Я реалізую мову програмування в Python - функції, визначені бібліотекою, використовують статичний execute
метод, де визначені користувачем функції вимагають аргументи екземпляра (тобто тіло функції). Цей декоратор виключає попередження про "невикористаний параметр" у інспектора 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
не має неявних аргументів.
Офіційні документи python:
Метод класу отримує клас як неявний перший аргумент, подібно до того, як метод екземпляра отримує екземпляр. Щоб оголосити метод класу, використовуйте цю ідіому:
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 функція також може викликатись без ініціювання класу, але її визначення слід за підкласом, а не з батьківського класу шляхом успадкування. Це тому, що перший аргумент функції @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
. Це саме називається " метод примірника ". Якщо використовується метод екземпляра, він використовується як часткова функція (на відміну від загальної функції, визначеної для всіх значень при перегляді у вихідному коді), тобто при використанні перший із аргументів визначений як екземпляр об'єкта, з усіма його заданими ознаками. У нього є примірник об'єкта, прив'язаного до нього, і його потрібно викликати з екземпляра об'єкта. Зазвичай він отримує доступ до різних атрибутів екземпляра.
Наприклад, це примірник рядка:
', '
якщо ми використовуємо метод екземпляра 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'>
Дивіться вихідний код панди для інших подібних прикладів альтернативних конструкторів, а також дивіться також офіційну документацію 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="")
замість classmethod get_person
. Також результат буде точно таким же в цьому випадку.
Я думаю, що краще питання "Коли ви використовуєте @classmethod vs @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)
Також зауважте, що це хороший приклад використання класного методу та статичного методу. Статичний метод явно належить до класу, оскільки він використовує кластер Кластер внутрішньо. Класовому методу потрібна лише інформація про клас, а не примірник об'єкта.
Ще однією перевагою перетворення _is_cluster_for
методу classmethod є те, що підклас може вирішити змінити його реалізацію, можливо, тому, що він є досить загальним і може обробляти більше ніж один тип кластеру, тому просто перевірити назву класу було б недостатньо.
Статичні методи:
Переваги статичних методів:
Більш зручно імпортувати порівняно з функціями на рівні модуля, оскільки кожен метод не повинен бути спеціально імпортований
@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',)
Ще одне врахування щодо staticmethod vs classmethod стосується спадкування. Скажімо, у вас такий клас:
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
Спробую пояснити основну різницю на прикладі.
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 - ми можемо безпосередньо викликати статичні та класні методи без ініціалізації
# A.run_self() # wrong
A.run_static()
A.run_class()
2- Статичний метод не може викликати самометод, але може викликати інші статичні та класичні методи
3- Статичний метод належить до класу і взагалі не буде використовувати об'єкт.
4- Класовий метод пов'язаний не з об'єктом, а з класом.
@classmethod: може використовуватися для створення спільного глобального доступу до всіх екземплярів, створених цього класу ..... як оновлення запису декількома користувачами .... Я, зокрема, знайшов, що він також використовує ful під час створення синглів.: )
@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
декоратор.
Інші випадки були добре висвітлені безвідмовною відповіддю.
Метод класу отримує клас як неявний перший аргумент, подібно до того, як метод екземпляра отримує екземпляр. Це метод, який пов'язаний з класом, а не об'єктом класу. Він має доступ до стану класу, оскільки він приймає параметр класу, який вказує на клас, а не на об’єкт об'єкта. Він може змінити стан класу, який застосовуватиметься до всіх примірників класу. Наприклад, він може змінити змінну класу, яка буде застосована до всіх примірників.
З іншого боку, статичний метод не отримує неявного першого аргументу, порівняно з методами класу або методами екземпляра. І не може отримати доступ або змінити стан класу. Він належить лише до класу, оскільки з точки зору дизайну це правильний шлях. Але з точки зору функціональності не пов'язаний, під час виконання, з класом.
в якості керівництва використовуйте статичні методи як утиліти, використовуйте методи класу, наприклад, як заводські. А може, визначити синглтон. І використовуйте методи екземпляра для моделювання стану та поведінки екземплярів.
Сподіваюся, я зрозумів!
Мій внесок демонструє різницю серед @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 в буквальному сенсі, надаючи різні уявлення.
Нормальний метод класу - це неявний динамічний метод, який приймає екземпляр як перший аргумент.
Навпаки, статичний метод не приймає екземпляр як перший аргумент, тому його називають "статичним" .
Статичний метод дійсно є такою нормальною функцією, такою ж, як і поза визначенням класу.
Він, на щастя, згрупований у клас саме для того, щоб стояти ближче, де він застосований, або ви можете прокрутити його, щоб знайти його.
Я думаю, що надання чисто 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
в ньому. Staticmethod не може отримати доступ до нічого з попереднього. Класметод може отримати доступ .b==2
лише через cls.b
. Нормальний метод може отримати доступ як .b==4
через, так self.b
і .b==2
через self.__class__.b
.
Ми можемо дотримуватися стилю KISS (будьте простим, дурним): не використовуйте staticmethods та classmethods, не використовуйте класи без інстанції, отримуйте доступ лише до атрибутів об'єкта self.attribute(s)
. Є мови, де OOP реалізований таким чином, і я думаю, що це не погана ідея. :)
Швидкий зліт інших ідентичних методів в iPython виявляє, що @staticmethod
приносить граничний приріст продуктивності (у наносекундах), але в іншому випадку, здається, він не виконує жодної функції. Крім того, будь-які підвищення продуктивності, ймовірно, буде вимкнено додатковою роботою по обробці методу staticmethod()
під час компіляції (що відбувається до будь-якого виконання коду при запуску сценарію).
Ради читабельності коду я б уникав, @staticmethod
якщо ваш метод не буде використовуватися для навантажень роботи, де наносекунди рахуються.