Значение @classmethod и @staticmethod для начинающих?
может кто-нибудь объяснить мне значение @classmethod
и @staticmethod
в python? Мне нужно знать разницу и смысл.
насколько я понимаю, @classmethod
говорит классу, что это метод, который должен быть унаследован на подклассы, или... что-то. Тем не менее, какой смысл этого? Почему бы просто не определить метод класса без добавления @classmethod
или @staticmethod
и @
определения?
tl; dr:, когда должен ли я использовать их,почему я должен использовать их, и как должен ли я их использовать?
Я довольно продвинутый с C++, поэтому использование более продвинутых концепций программирования не должно быть проблемой. Не стесняйтесь давать мне соответствующий пример C++, если это возможно.
11 ответов:
хотя
classmethod
иstaticmethod
очень похожи, есть небольшая разница в использовании для обеих сущностей:classmethod
должен иметь ссылку на объект класса в качестве первого параметра, в то время какstaticmethod
не может иметь параметров.пример
class Date(object): def __init__(self, day=0, month=0, year=0): self.day = day self.month = month self.year = year @classmethod def from_string(cls, date_as_string): day, month, year = map(int, date_as_string.split('-')) date1 = cls(day, month, year) return date1 @staticmethod def is_date_valid(date_as_string): day, month, year = map(int, date_as_string.split('-')) return day <= 31 and month <= 12 and year <= 3999 date2 = Date.from_string('11-09-2012') is_date = Date.is_date_valid('11-09-2012')
объяснение
предположим пример класса, имеющего дело с информацией о дате (это будет наш шаблон):
class Date(object): def __init__(self, day=0, month=0, year=0): self.day = day self.month = month self.year = year
этот класс, очевидно, может быть использован для хранения информация о некоторых датах (без информации о часовом поясе; предположим, что все даты представлены в формате UTC).
вот и мы
__init__
, типичный инициализатор экземпляров класса Python, который получает аргументы как типичныйinstancemethod
, имея первый необязательный аргумент (self
), который содержит ссылку на вновь созданный экземпляр.Метод Класса
у нас есть некоторые задачи, которые можно красиво сделать с помощью
classmethod
s.предположим, что мы хотим создать большое
Date
экземпляры класса, имеющие информацию о дате, поступающую из внешнего источника, закодированного в виде строки с форматом 'dd-mm-yyyy'. Предположим, что мы должны сделать это в разных местах в исходном коде нашего проекта.Итак, что мы должны сделать вот это:
- проанализируйте строку для получения дня, месяца и года в виде трех целочисленных переменных или кортежа из 3 элементов, состоящего из этого переменная.
- Instantiate
Date
путем передачи этих значений в вызов инициализации.это будет выглядеть так:
day, month, year = map(int, string_date.split('-')) date1 = Date(day, month, year)
для этой цели C++ может реализовать такую функцию с перегрузкой, но Python не хватает этой перегрузки. Вместо этого мы можем использовать
classmethod
. Давайте создадим еще один "конструктор".@classmethod def from_string(cls, date_as_string): day, month, year = map(int, date_as_string.split('-')) date1 = cls(day, month, year) return date1 date2 = Date.from_string('11-09-2012')
давайте более внимательно рассмотрим вышеописанную реализацию и рассмотрим, какие преимущества у нас есть здесь:
- мы реализовали разбор строки даты в одном месте, и теперь он повторно используется.
- инкапсуляция отлично работает здесь (Если вы думаете, что можете реализовать синтаксический анализ строк как одну функцию в другом месте, это решение гораздо лучше подходит для парадигмы ООП).
cls
- это объект, который содержит класс, а не экземпляр класса. Это довольно круто, потому что если мы наследуем нашDate
класс, все дети будут иметьfrom_string
определено также.статический метод
а как же
staticmethod
? Это очень похоже наclassmethod
но не принимает никаких обязательных параметров (например, метод класса или метод экземпляра).давайте посмотрим на следующий случай использования.
у нас есть строка даты, которую мы хотим как-то проверить. Эта задача также логически связана с
гдеDate
класс, который мы использовали до сих пор, но не требует создания экземпляра оно.staticmethod
может быть полезно. Давайте посмотрим на следующий фрагмент кода:@staticmethod def is_date_valid(date_as_string): day, month, year = map(int, date_as_string.split('-')) return day <= 31 and month <= 12 and year <= 3999 # usage: is_date = Date.is_date_valid('11-09-2012')
Итак, как мы видим, от использования
staticmethod
, у нас нет доступа к тому, что такое класс-это в основном просто функция, называемая синтаксически как метод, но без доступа к объекту и его внутренним элементам (полям и другим методам), в то время как classmethod делает.
ответ Ростислава Дзинько очень уместен. Я подумал, что могу выделить еще одну причину, по которой вы должны выбрать
@classmethod
over@staticmethod
при создании дополнительного конструктора.в приведенном выше примере, Ростислав используется
@classmethod
from_string
как фабрика для созданияDate
объекты из других неприемлемых параметров. То же самое можно сделать с@staticmethod
как показано в коде ниже:иclass Date: def __init__(self, month, day, year): self.month = month self.day = day self.year = year def display(self): return "{0}-{1}-{2}".format(self.month, self.day, self.year) @staticmethod def millenium(month, day): return Date(month, day, 2000) new_year = Date(1, 1, 2013) # Creates a new Date object millenium_new_year = Date.millenium(1, 1) # also creates a Date object. # Proof: new_year.display() # "1-1-2013" millenium_new_year.display() # "1-1-2000" isinstance(new_year, Date) # True isinstance(millenium_new_year, Date) # True
new_year
иmillenium_new_year
несколько экземпляровDate
класса.но, если вы внимательно наблюдаете, Заводской процесс жестко закодирован для создания
Date
объекты, несмотря ни на что. Это означает, что даже еслиDate
класс является подклассом, подклассы по-прежнему будут создавать простыеDate
объект (без какого-либо свойства подкласса). Смотрите это в примере ниже:class DateTime(Date): def display(self): return "{0}-{1}-{2} - 00:00:00PM".format(self.month, self.day, self.year) datetime1 = DateTime(10, 10, 1990) datetime2 = DateTime.millenium(10, 10) isinstance(datetime1, DateTime) # True isinstance(datetime2, DateTime) # False datetime1.display() # returns "10-10-1990 - 00:00:00PM" datetime2.display() # returns "10-10-2000" because it's not a DateTime object but a Date object. Check the implementation of the millenium method on the Date class
datetime2
не является экземпляромDateTime
? WTF? Ну это из-за@staticmethod
декоратор используется.в большинстве случаев, это нежелательно. Если вам нужен Заводской метод, который знает класс, который его вызвал, то
@classmethod
это то, что вам нужно.переписывание
Date.millenium
as (это единственная часть кода выше, которая изменяется)@classmethod def millenium(cls, month, day): return cls(month, day, 2000)
обеспечивает
class
не жестко, а скорее узнал.cls
может быть любой подкласс. В результатеobject
по праву будет примеромcls
. Давайте проверим это.datetime1 = DateTime(10, 10, 1990) datetime2 = DateTime.millenium(10, 10) isinstance(datetime1, DateTime) # True isinstance(datetime2, DateTime) # True datetime1.display() # "10-10-1990 - 00:00:00PM" datetime2.display() # "10-10-2000 - 00:00:00PM"
причина в том, как вы знаете к настоящему времени,
@classmethod
вместо@staticmethod
@classmethod
означает: когда этот метод вызывается, мы передаем класс в качестве первого аргумента, а не экземпляра этого класса (как мы обычно делаем с методами). Это означает, что вы можете использовать класс и его свойства внутри метода, а не конкретного экземпляра.
@staticmethod
означает: когда этот метод вызывается, мы не передаем экземпляр класса (как мы обычно делаем с методами). Это означает, что вы можете поставить функцию внутри класса, но вы не можете получить доступ к экземпляру этот класс (это полезно, когда ваш метод не использует экземпляр).
когда использовать каждый
Это не более чем функция, определенная внутри класса. Он вызывается без создания экземпляра класса в первую очередь. Это определение является неизменным через наследование.
- Python не должен создавать экземпляр bound-method для объекта.
- это облегчает читаемость кода: видя @staticmethod, мы знаем, что метод не зависит от состояния объекта сам;
@classmethod
функция также вызывается без создания экземпляра класса, но ее определение следует за подклассом, а не родительский класс, через наследование, может быть переопределен подклассом. Это потому, что первый аргумент для@classmethod
функция всегда должна бытьcls (class)
.
- методы фабрики, которые используются для создания экземпляра класса, используя например какой-то предварительной обработки.
- вызов статических методов статические методы: если вы разделяете статические методы на несколько статических методов, вы не должны жестко кодировать имя класса, но использовать методы класса
здесь хорошая ссылка на эту тему.
можно было бы использовать
@classmethod
когда он / она захочет изменить поведение метода, основанного на том, какой подкласс вызывает метод. помните, что у нас есть ссылка на вызывающий класс в методе класса.при использовании статического вы хотели бы, чтобы поведение оставалось неизменным в подклассах
пример:
class Hero: @staticmethod def say_hello(): print("Helllo...") @classmethod def say_class_hello(cls): if(cls.__name__=="HeroSon"): print("Hi Kido") elif(cls.__name__=="HeroDaughter"): print("Hi Princess") class HeroSon(Hero): def say_son_hello(self): print("test hello") class HeroDaughter(Hero): def say_daughter_hello(self): print("test hello daughter") testson = HeroSon() testson.say_class_hello() #Output: "Hi Kido" testson.say_hello() #Outputs: "Helllo..." testdaughter = HeroDaughter() testdaughter.say_class_hello() #Outputs: "Hi Princess" testdaughter.say_hello() #Outputs: "Helllo..."
немного компиляция
@staticmethod Способ записи метода внутри класса без ссылки на объект, на который он вызывается. Поэтому нет необходимости передавать неявный аргумент, такой как self или cls. Он написан точно так же, как написано вне класса, но это не бесполезно в python, потому что если вам нужно инкапсулировать метод внутри класса, так как этот метод должен быть частью этого класса @staticmethod, это удобно это дело.
@classmethod Это важно, когда вы хотите написать заводской метод и с помощью этого пользовательского атрибута(ов) могут быть присоединены в классе. Этот атрибут(ы) может быть переопределен в наследуемом классе.
сравнение между этими двумя методами может быть ниже
смысл
@classmethod
и@staticmethod
?
- метод-это функция в пространстве имен объекта, доступное в качестве атрибута.
- обычный (т. е. экземпляр) метод получает экземпляр (мы обычно называем его
self
) в качестве неявного первого аргумента.- A класс метод получает класс (мы обычно называем это
cls
) в качестве неявного первого аргумента.- A статический метод не получает неявного первого аргумента (как обычная функция).
когда я должен использовать их, почему я должен использовать их, и как я должен использовать их?
не нужно либо декоратор. Но по принципу, что вы должны минимизировать количество аргументов для функций (см. Clean Coder), они полезны именно для этого.
class Example(object): def regular_instance_method(self): """A function of an instance has access to every attribute of that instance, including its class (and its attributes.) Not accepting at least one argument is a TypeError. Not understanding the semantics of that argument is a user error. """ return some_function_f(self) @classmethod def a_class_method(cls): """A function of a class has access to every attribute of the class. Not accepting at least one argument is a TypeError. Not understanding the semantics of that argument is a user error. """ return some_function_g(cls) @staticmethod def a_static_method(): """A static method has no information about instances or classes unless explicitly given. It just lives in the class (and thus its instances') namespace. """ return some_function_h()
для обоих методов экземпляра и методы класса, не принимающие хотя бы один аргумент, являются TypeError, но не понимающие семантику этого аргумента-это ошибка пользователя.
(определение
some_function
's, например:some_function_h = some_function_g = some_function_f = lambda x=None: x
и это будет работать.)
точечный поиск по экземплярам и классам:
точечный поиск на экземпляре выполняется в следующем порядке-мы ищем:
- дескриптор данных в пространстве имен класса (как свойство)
- данные в экземпляре
__dict__
- дескриптор без данных в пространстве имен класса (методы).
обратите внимание, точечный поиск на экземпляре вызывается следующим образом:
instance = Example() instance.regular_instance_method
и методы вызываются атрибуты:
instance.regular_instance_method()
методы экземпляра
аргумент
self
, неявно задается через точечный поиск.вы должны получить доступ к методам экземпляра экземпляров класс.
>>> instance = Example() >>> instance.regular_instance_method() <__main__.Example object at 0x00000000399524E0>
методы класса
аргумент
cls
, неявно задается через точечный поиск.вы можете получить доступ к этому методу через экземпляр класса (или подкласса).
>>> instance.a_class_method() <class '__main__.Example'> >>> Example.a_class_method() <class '__main__.Example'>
статические методы
никакие аргументы не приводятся неявно. Этот метод работает как любая функция, определенная (например) в пространстве имен модулей, за исключением того, что ее можно найти
>>> print(instance.a_static_method()) None
опять же, когда я должен использовать их, почему я должен их использовать?
каждый из них постепенно более ограничен в информации, которую они передают методу по сравнению с методами экземпляра.
используйте их, когда вам не нужна информация.
это делает ваши функции и методы легче рассуждать и unittest.
что проще?
def function(x, y, z): ...
или
def function(y, z): ...
или
def function(z): ...
функции с меньше аргументов проще. Они также легче unittest.
они сродни экземпляру, классу и статическим методам. Имея в виду, что когда у нас есть экземпляр, у нас также есть его класс, снова спросите себя, о чем легче рассуждать?:
def an_instance_method(self, arg, kwarg=None): cls = type(self) # Also has the class of instance! ... @classmethod def a_class_method(cls, arg, kwarg=None): ... @staticmethod def a_static_method(arg, kwarg=None): ...
примеры строение
вот несколько моих любимых встроенных примеров:
The
str.maketrans
статический метод был функцией вstring
модуль, но гораздо более удобно, чтобы он был доступен изstr
пространство имен.>>> 'abc'.translate(str.maketrans({'a': 'b'})) 'bbc'
The
dict.fromkeys
метод класса возвращает новый словарь, созданный из итерируемых ключей:>>> dict.fromkeys('abc') {'a': None, 'c': None, 'b': None}
при подклассе мы видим, что он получает информацию о классе как метод класса, что очень полезно:
>>> class MyDict(dict): pass >>> type(MyDict.fromkeys('abc')) <class '__main__.MyDict'>
мой совет - вывод
используйте статические методы, когда вам не нужны аргументы класса или экземпляра, но функция связана с использования объекта, и это удобно для функции, чтобы быть в пространстве имен объекта.
используйте методы класса, когда вам не нужна информация об экземпляре, но нужна информация о классе, возможно, для другого класса или статических методов, или, возможно, сама в качестве конструктора. (Вы не будете жестко кодировать класс, чтобы подклассы могли использоваться здесь.)
Я новичок на этом сайте, я прочитал все ответы выше, и получил информацию, что я хочу. Однако я не имею права выдвигать свою кандидатуру. Поэтому я хочу начать свой StackOverflow с ответа, как я его понимаю.
@staticmethod
не нужно self или cls в качестве первого параметра метода@staticmethod
и@classmethod
обернутая функция может быть вызвана переменной экземпляра или класса@staticmethod
украшенная функция влияет на какой-то вид "неизменяемое свойство", которое наследование подкласса не может перезаписать его функцию базового класса, которая обернута@staticmethod
оформителя.@classmethod
нужен cls (имя класса, вы можете изменить имя переменной, если хотите, но это не рекомендуется) в качестве первого параметра функции@classmethod
всегда используется способом подкласса, наследование подкласса может изменить эффект функции базового класса, т. е.@classmethod
обернутая функция базового класса может быть перезаписана различными подклассы.
немного другой способ думать об этом, что может быть полезным для кого-то... Метод класса используется в суперклассе для определения того, как этот метод должен вести себя, когда он вызывается различными дочерними классами. Статический метод используется, когда мы хотим вернуть то же самое независимо от дочернего класса, который мы вызываем.
короче говоря, @classmehtod превращает обычный метод в заводской метод.
давайте рассмотрим его на примере:
class PythonBook: def __init__(self, name, author): self.name = name self.author = author def __repr__(self): return f'Book: {self.name}, Author: {self.author}'
без @classmethod, вы должны работать, чтобы создать экземпляры один за другим, и они scartted.
book1 = PythonBook('Learning Python', 'Mark Lutz') In [20]: book1 Out[20]: Book: Learning Python, Author: Mark Lutz book2 = PythonBook('Python Think', 'Allen B Dowey') In [22]: book2 Out[22]: Book: Python Think, Author: Allen B Dowey
как например с @classmethod
class PythonBook: def __init__(self, name, author): self.name = name self.author = author def __repr__(self): return f'Book: {self.name}, Author: {self.author}' @classmethod def book1(cls): return cls('Learning Python', 'Mark Lutz') @classmethod def book2(cls): return cls('Python Think', 'Allen B Dowey')
метод класса может изменять состояние класса, он привязан к классу и содержит cls в качестве параметра.
статический метод не может изменить состояние класса, он привязан к классу и не знает класса или экземпляра
class empDetails: def __init__(self,name,sal): self.name=name self.sal=sal @classmethod def increment(cls,name,none): return cls('yarramsetti',6000 + 500) @staticmethod def salChecking(sal): return sal > 6000 emp1=empDetails('durga prasad',6000) emp2=empDetails.increment('yarramsetti',100) # output is 'durga prasad' print emp1.name # output put is 6000 print emp1.sal # output is 6500,because it change the sal variable print emp2.sal # output is 'yarramsetti' it change the state of name variable print emp2.name # output is True, because ,it change the state of sal variable print empDetails.salChecking(6500)