Значение @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), который содержит ссылку на вновь созданный экземпляр.

Метод Класса

у нас есть некоторые задачи, которые можно красиво сделать с помощью classmethods.

предположим, что мы хотим создать большое Date экземпляры класса, имеющие информацию о дате, поступающую из внешнего источника, закодированного в виде строки с форматом 'dd-mm-yyyy'. Предположим, мы должны сделать это в разных местах исходного кода нашего проекта.

Итак, что мы должны сделать вот это:

  1. проанализируйте строку для получения дня, месяца и года в виде трех целочисленных переменных или кортежа из 3 элементов, состоящего из переменная.
  2. создать 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')

давайте более внимательно рассмотрим вышеуказанную реализацию и рассмотрим, какие преимущества у нас есть здесь:

  1. мы реализовали синтаксический анализ строки даты в одном месте, и теперь он многоразовый.
  2. инкапсуляция здесь отлично работает (если вы думаете, что можете реализовать синтаксический анализ строк как одну функцию в другом месте, это решение гораздо лучше подходит парадигме ООП).
  3. 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 над @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 object (без какого-либо свойства подкласса). См. это в примере ниже:

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 означает: когда этот метод вызывается, мы не передаем экземпляр класса (как мы обычно делаем с методами). Это означает, что вы можете поместить функцию внутри класса, но не можете получить доступ к экземпляру этот класс (это полезно, когда ваш метод не использует экземпляр).


когда использовать каждый

@staticmethod функция-это не что иное, как функция, определенная внутри класса. Он вызывается без создания экземпляра класса first. Это определение неизменяемо через наследование.

  • Python не должен создавать экземпляр bound-method для объекта.
  • это облегчает читаемость кода: видеть @staticmethod, мы знаем, что метод не зависит от состояния объекта сам;

@classmethod функция также вызывается без создания экземпляра класса, но ее определение следует за подклассом, а не родительским классом, через наследование, может быть переопределено подклассом. Это потому, что первый аргумент для @classmethod функция всегда должна быть cls (class).

  • методы фабрики, которые используются для создания экземпляра класса, используя, например, некоторую предварительную обработку.
  • вызов статических методов статические методы: если вы разделяете статические методы на несколько статических методов, вы не должны жестко кодировать имя класса, но использовать методы класса

здесь хорошая ссылка на эту тему.


можно было бы использовать @classmethod когда он / она захочет изменить поведение метода, на основе которого подкласс вызывает метод. помните, что у нас есть ссылка на вызывающий класс в методе class.

при использовании static вы хотели бы, чтобы поведение оставалось неизменным в подклассах

пример:

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 Это важно, когда вы хотите написать заводской метод и с помощью этого настраиваемого атрибута(ов) может быть присоединен в классе. Этот атрибут(ы) может быть переопределен в унаследованном классе.

сравнение между этими двумя методами может быть ниже

Table


смысл @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

и это будет работать.)

пунктирные поиски на экземплярах и классах:

точечный поиск на экземпляре выполняется в этом порядке - мы ищем:

  1. дескриптор данных в пространстве имен класса (как собственность)
  2. данные в экземпляре __dict__
  3. не-дескриптор данных в пространстве имен класса (методы).

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

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):
    ...

примеры строение

вот несколько моих любимых примеров сборки:

на str.maketrans статический метод был функцией в string модуль, но гораздо более удобным для него, чтобы быть доступным от str пространство имен.

>>> 'abc'.translate(str.maketrans({'a': 'b'}))
'bbc'

на dict.fromkeys метод class возвращает новый словарь, созданный из iterable ключей:

>>> 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')

Class метод может изменить состояние класса, он привязан к классу и содержит cls в качестве параметра.

статический метод не может изменить состояние класса, он привязан к классу и не знает class или instance

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)