Разница между staticmethod и classmethod

avatar
Daryl Spitzer
25 сентября 2008 в 21:01
888200
33
4025

В чем разница между функцией, украшенной @staticmethod, и функцией, украшенной @classmethod?

Источник
FistOfFury
6 декабря 2017 в 03:44
17

статические методы иногда лучше использовать в качестве функций уровня модуля в python ради чистоты. С помощью функции модуля легче импортировать только нужную функцию и предотвратить ненужное "." синтаксис (я смотрю на тебя Objective-C). Методы класса имеют большее применение, поскольку их можно использовать в сочетании с полиморфизмом для создания функций "фабричного шаблона". это потому, что методы класса получают класс как неявный параметр.

imsrgadich
11 июля 2018 в 15:02
37

tl; dr >> по сравнению с обычными методами, к статическим методам и методам класса также можно получить доступ с помощью класса, но в отличие от методов класса, статические методы неизменяемы через наследование.

moooeeeep
10 сентября 2018 в 09:35
4

Связанное выступление Раймонда Хеттингера по теме: youtube.com/watch?v=HTLu2DFOdTg

Robert Nowak
11 октября 2019 в 11:22
1

более точно youtube.com/watch?v=HTLu2DFOdTg&feature=youtu.be&t=2689 вам нужен только classmethod для альтернативных конструкторов. В противном случае вы можете использовать staticmethod и получить доступ к любому атрибуту класса (через. / Dot) с помощью более информативного фактического "CLASSNAME" вместо cls, как в classmethod

Ответы (33)

avatar
unutbu
3 ноября 2009 в 19:13
3480

Может быть, вам поможет небольшой пример кода: обратите внимание на разницу в сигнатуре вызовов foo, class_foo и static_foo:

class A(object):
    def foo(self, x):
        print(f"executing foo({self}, {x})")

    @classmethod
    def class_foo(cls, x):
        print(f"executing class_foo({cls}, {x})")

    @staticmethod
    def static_foo(x):
        print(f"executing static_foo({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 просто возвращает хорошая старая функция без привязки аргументов. 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>
Alcott
19 сентября 2011 в 03:08
211

Я не понимаю, в чем подвох использования статического метода. мы можем просто использовать простую функцию вне класса.

unutbu
19 сентября 2011 в 10:34
426

@Alcott: вы можете переместить функцию в класс, потому что она логически принадлежит классу. В исходном коде Python (например, multiprocessing, turtle, dist-packages) он используется для «скрытия» «закрытых» функций с одним подчеркиванием из пространства имен модуля. Однако его использование в значительной степени сконцентрировано всего в нескольких модулях - возможно, это указывает на то, что это в основном стилистическая вещь. Хотя я не смог найти ни одного примера этого, @staticmethod может помочь организовать ваш код, будучи переопределенным подклассами. Без него у вас были бы варианты функции, плавающие в пространстве имен модуля.

MestreLion
3 мая 2012 в 09:50
19

... вместе с некоторыми пояснениями по , где и , почему использовать методы экземпляра, класса или статические. Вы не сказали ни слова об этом, но ОП не спрашивал об этом.

MestreLion
3 мая 2012 в 09:55
123

@Alcott: как сказал unutbu, статические методы - это организационная / стилистическая особенность. Иногда у модуля есть много классов, и некоторые вспомогательные функции логически привязаны к определенному классу, а не к другим, поэтому имеет смысл не «засорять» модуль множеством «бесплатных функций», и лучше использовать статические метод, чем полагаться на плохой стиль смешивания классов и определений функций вместе в коде, чтобы показать, что они «связаны»

avatar
Arpan Saini
20 июля 2021 в 18:18
0

Определение статического метода и метода класса из его документации. и Когда использовать статический метод, а когда - метод класса.

  • Статический метод похож на статический метод в java и C #, он не будет использовать какое-либо значение инициализации класса, все, что ему нужно извне для нормальной работы.

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

статический метод (функция) -> метод

Convert a function to be a static method.

A static method does not receive an implicit first argument.
To declare a static method, use this idiom:

     class C:
         @staticmethod
         def f(arg1, arg2, ...):
             ...

It can be called either on the class (e.g. C.f()) or on an instance
(e.g. C().f()).  The instance is ignored except for its class.

Static methods in Python are similar to those found in Java or C++.
For a more advanced concept, see the classmethod builtin.
"""

метод класса (функция) -> метод

Convert a function to be a class method.

A class method receives the class as implicit first argument,
just like an instance method receives the instance.
To declare a class method, use this idiom:

  class C:
      @classmethod
      def f(cls, arg1, arg2, ...):
          ...

It can be called either on the class (e.g. C.f()) or on an instance
(e.g. C().f()).  The instance is ignored except for its class.
If a class method is called for a derived class, the derived class
object is passed as the implied first argument.

Class methods are different than C++ or Java static methods.
If you want those, see the staticmethod builtin.
avatar
H.H
17 марта 2021 в 11:25
5

Разница возникает при наследовании.

Предположим, что существует два класса - родительский и дочерний. Если кто-то хочет использовать @staticmethod, метод print_name должен быть написан дважды, потому что имя класса должно быть написано в строке печати. ​​

class Parent:
   _class_name = "Parent"

   @staticmethod
   def print_name():
       print(Parent._class_name)


class Child(Parent):
   _class_name = "Child"

   @staticmethod
   def print_name():
       print(Child._class_name)


Parent.print_name()
Child.print_name()

Однако для @classmethod не требуется писать метод print_name дважды.

class Parent:
    _class_name = "Parent"

    @classmethod
    def print_name(cls):
        print(cls._class_name)


class Child(Parent):
    _class_name = "Child"


Parent.print_name()
Child.print_name()
masih
29 июля 2021 в 12:34
0

Очень понравилась простота этого ответа. Wish мог бы отдать за это 100 голосов.

avatar
Giorgos Myrianthous
4 февраля 2021 в 16:00
1

Сначала давайте начнем с примера кода, который мы будем использовать для понимания обеих концепций:

class Employee:

    NO_OF_EMPLOYEES = 0
  
    def __init__(self, first_name, last_name, salary):
        self.first_name = first_name
        self.last_name = last_name
        self.salary = salary
        self.increment_employees()

    def give_raise(self, amount):
        self.salary += amount

    @classmethod
    def employee_from_full_name(cls, full_name, salary):
        split_name = full_name.split(' ')
        first_name = split_name[0]
        last_name = split_name[1]
        return cls(first_name, last_name, salary)

    @classmethod
    def increment_employees(cls):
        cls.NO_OF_EMPLOYEES += 1

    @staticmethod
    def get_employee_legal_obligations_txt():
        legal_obligations = """
        1. An employee must complete 8 hours per working day
        2. ...
        """
        return legal_obligations

Метод класса

Метод класса принимает сам класс как неявный аргумент и - необязательно - любые другие аргументы, указанные в определении. Важно понимать, что метод класса не имеет доступа к экземплярам объекта (в отличие от методов экземпляра). Следовательно, методы класса не могут использоваться для изменения состояния экземпляра объекта, но вместо этого они могут изменять состояние класса, которое является общим для всех экземпляров этого класса. Методы класса обычно полезны, когда нам нужно получить доступ к самому классу - например, когда мы хотим создать фабричный метод, то есть метод, который создает экземпляры класса. Другими словами, методы класса могут служить альтернативными конструкторами.

В нашем примере кода экземпляр Employee может быть создан с помощью трех аргументов; first_name, last_name и salary.

employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.first_name)
print(employee_1.salary)

'Andrew'
85000

Теперь предположим, что есть вероятность, что имя сотрудника может быть указано в одном поле, в котором имя и фамилия разделены пробелом. В этом случае мы могли бы использовать наш метод класса с именем employee_from_full_name, который принимает всего три аргумента. Первый - это сам класс, который является неявным аргументом, что означает, что он не будет предоставлен при вызове метода - Python автоматически сделает это за нас:

employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(employee_2.first_name)
print(employee_2.salary)

'John'
95000

Обратите внимание, что также можно вызвать employee_from_full_name из экземпляров объекта, хотя в данном контексте это не имеет большого смысла:

employee_1 = Employee('Andrew', 'Brown', 85000)
employee_2 = employee_1.employee_from_full_name('John Black', 95000)

Еще одна причина, по которой мы можем захотеть создать метод класса, - это когда нам нужно изменить состояние класса. В нашем примере переменная класса NO_OF_EMPLOYEES отслеживает количество сотрудников, работающих в настоящее время в компании. Этот метод вызывается каждый раз, когда создается новый экземпляр Employee, и он соответственно обновляет счетчик:

employee_1 = Employee('Andrew', 'Brown', 85000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')
employee_2 = Employee.employee_from_full_name('John Black', 95000)
print(f'Number of employees: {Employee.NO_OF_EMPLOYEES}')

Number of employees: 1
Number of employees: 2

Статические методы

С другой стороны, в статических методах ни экземпляр (т.е. self), ни сам класс (т.е. cls) не передаются в качестве неявного аргумента. Это означает, что такие методы не могут получить доступ к самому классу или его экземплярам. Теперь можно утверждать, что статические методы бесполезны в контексте классов, поскольку они также могут быть помещены во вспомогательные модули вместо того, чтобы добавлять их в качестве членов класса. В объектно-ориентированном программировании важно структурировать классы в логические блоки, и поэтому статические методы весьма полезны, когда нам нужно добавить метод в класс просто потому, что он логически принадлежит классу. В нашем примере статический метод с именем get_employee_legal_obligations_txt просто возвращает строку, содержащую юридические обязательства каждого отдельного сотрудника компании. Эта функция не взаимодействует ни с самим классом, ни с каким-либо экземпляром. Его можно было бы поместить в другой вспомогательный модуль, однако он имеет отношение только к этому классу, и поэтому мы должны поместить его в класс Employee.

Статический метод может быть доступен непосредственно из самого класса

print(Employee.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

или из экземпляра класса:

employee_1 = Employee('Andrew', 'Brown', 85000)
print(employee_1.get_employee_legal_obligations_txt())


    1. An employee must complete 8 hours per working day
    2. ...

Ссылки

avatar
Sia
3 февраля 2021 в 03:32
0

TLDR;

staticmethod, по сути, является функцией, привязанной к классу (и, следовательно, к его экземплярам)

classmethod по сути является наследуемым staticmethod.

Подробнее см. В отличных ответах других.

avatar
illuminato
16 января 2021 в 19:58
0

Python имеет несколько встроенных декораторов. Большая тройка:

@classmethod
@staticmethod
@property

Декоратор @classmethod может быть вызван с экземпляром класса или непосредственно самим классом в качестве его первого аргумента.

@staticmethod - это способ поместить функцию в класс (потому что она логически принадлежит ему), указывая, что она не требует доступа к классу.

Рассмотрим следующий класс:

class DecoratorTest(object):

    def __init__(self):
        pass

    def doubler(self, x):
        print("running doubler")
        return x*2

    @classmethod
    def class_doubler(klass, x):
        print("running doubler: %s" % klass)
        return x*2

    @staticmethod
    def static_doubler(x):
        print("running quad")
        return x*2

decor = DecoratorTest()

Посмотрим, как это работает:

print(decor.doubler(5))
# running doubler
# 10

print(decor.class_doubler(5))
# running doubler: <class '__main__.DecoratorTest'> 
# 10
print(DecoratorTest.class_doubler(5))
# running doubler: <class '__main__.DecoratorTest'> 
# 10

print(DecoratorTest.static_doubler(5))
# running doubler 
# 10
print(decor.static_doubler(5))
# running doubler 
# 10

print(decor.doubler)
# <bound method DecoratorTest.doubler of <__main__.DecoratorTest object at 0x7f90e74fd150>> 
print(decor.class_doubler)
# <bound method DecoratorTest.class_doubler of <class '__main__.DecoratorTest'>> 
print(decor.static_doubler)
# <function DecoratorTest.static_doubler at 0x7f90e7447440> 
avatar
Milovan Tomašević
7 ноября 2020 в 20:16
5

Метод экземпляра :

+ Может изменять состояние экземпляра объекта

+ Может изменить состояние класса

Метод класса :

- Невозможно изменить состояние экземпляра объекта

+ Может изменить состояние класса

Статический метод :

- Невозможно изменить состояние экземпляра объекта

- Невозможно изменить состояние класса

class MyClass:
    ''' 
    Instance method has a mandatory first attribute self which represent the instance itself. 
    Instance method must be called by a instantiated instance.
    '''
    def method(self):
        return 'instance method called', self
    
    '''
    Class method has a mandatory first attribute cls which represent the class itself. 
    Class method can be called by an instance or by the class directly. 
    Its most common using scenario is to define a factory method.
    '''
    @classmethod
    def class_method(cls):
        return 'class method called', cls
    
    '''
    Static method doesn’t have any attributes of instances or the class. 
    It also can be called by an instance or by the class directly. 
    Its most common using scenario is to define some helper or utility functions which are closely relative to the class.
    '''
    @staticmethod
    def static_method():
        return 'static method called'


obj = MyClass()
print(obj.method())
print(obj.class_method()) # MyClass.class_method()
print(obj.static_method()) # MyClass.static_method()

вывод:

('instance method called', <__main__.MyClass object at 0x100fb3940>)
('class method called', <class '__main__.MyClass'>)
static method called

Метод экземпляра, у нас действительно был доступ к экземпляру объекта, так что это был экземпляр объекта моего класса, тогда как с методом класса у нас есть доступ к самому классу. Но не ни к одному из объектов, потому что метод класса на самом деле не заботится о существующем объекте. Однако вы можете вызывать как метод класса, так и статический метод для экземпляра объекта. Это сработает, на самом деле это не имеет значения, поэтому снова, когда вы вызываете статический метод здесь, он будет работать, и он будет знать, какой метод вы хотите вызвать.

Статические методы используются для выполнения некоторых служебных задач, а методы классов используются для заводских методов. Фабричные методы могут возвращать объекты класса для различных вариантов использования.

И, наконец, короткий пример для лучшего понимания:

class Student:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_from_string(cls, name_string: str):
        first_name, last_name = name_string.split()
        if Student.validate_name(first_name) and Student.validate_name(last_name):
            return cls(first_name, last_name)
        else:
            print('Invalid Names')

    @staticmethod
    def validate_name(name):
        return len(name) <= 10


coderhelper_student = Student.get_from_string('Name Surname')
print(coderhelper_student.first_name) # Name
print(coderhelper_student.last_name) # Surname
avatar
Ale
31 июля 2020 в 18:12
1

При создании подклассов возникает одно довольно важное практическое различие. Если не возражаете, я украду пример @unutbu:

class A: 
    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)

class B(A):
    pass

В class_foo метод знает, для какого класса он вызван:

A.class_foo(1)
# => executing class_foo(<class '__main__.A'>  1)
B.class_foo(1)
# => executing class_foo(<class '__main__.B'>  1)

В static_foo нет способа определить, вызывается ли он на A или B:

A.static_foo(1)
# => executing static_foo(1)
B.static_foo(1)
# => executing static_foo(1)

Обратите внимание, что это не означает, что вы не можете использовать другие методы в staticmethod, вам просто нужно напрямую ссылаться на класс, что означает, что статические методы подклассов по-прежнему будут ссылаться на родительский класс:

class A:
    @classmethod
    def class_qux(cls, x):
        print(f"executing class_qux({cls}, {x})")
    
    @classmethod
    def class_bar(cls, x):
        cls.class_qux(x)

    @staticmethod
    def static_bar(x):
        A.class_qux(x)

class B(A):
    pass

A.class_bar(1)
# => executing class_qux(<class '__main__.A'>  1)
B.class_bar(1)
# => executing class_qux(<class '__main__.B'>  1)
A.static_bar(1)
# => executing class_qux(<class '__main__.A'>  1)
B.static_bar(1)
# => executing class_qux(<class '__main__.A'>  1)
avatar
mirek
2 марта 2020 в 16:01
0

статический метод не имеет доступа к атрибутам объекта, класса или родительских классов в иерархии наследования. Его можно вызвать непосредственно в классе (без создания объекта).

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

mirek
2 марта 2020 в 16:14
0

Еще одна важная вещь для методов класса: если вы измените атрибут в методе класса, все существующие объекты этого класса, которые явно не устанавливают этот атрибут, будут иметь измененное значение.

joel
13 августа 2020 в 21:18
0

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

avatar
Nicolae Petridean
20 ноября 2019 в 11:48
7

Метод класса получает класс как неявный первый аргумент, точно так же, как метод экземпляра получает экземпляр. Это метод, который привязан к классу, а не к объекту класса. Он имеет доступ к состоянию класса, поскольку принимает параметр класса, указывающий на класс, а не на экземпляр объекта. Он может изменять состояние класса, которое будет применяться ко всем экземплярам класса. Например, он может изменить переменную класса, которая будет применима ко всем экземплярам.

С другой стороны, статический метод не получает неявный первый аргумент по сравнению с методами класса или методами экземпляра. И не может получить доступ или изменить состояние класса. Он принадлежит к классу только потому, что с точки зрения дизайна это правильный путь. Но с точки зрения функциональности не привязан во время выполнения к классу.

в качестве руководства используйте статические методы в качестве утилит, используйте методы класса, например, в качестве фабрики. Или, может быть, определить синглтон. И используйте методы экземпляра для моделирования состояния и поведения экземпляров.

Надеюсь, я был чист!

avatar
Jacky1205
20 ноября 2019 в 09:50
3

Я думаю, что использование чисто 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
avatar
blue_note
21 мая 2019 в 10:47
34

Отличается только первый аргумент :

  • обычный метод: текущий объект автоматически передается как (дополнительный) первый аргумент
  • classmethod: класс текущего объекта автоматически передается как (дополнительный) первый аргумент
  • статический метод: никакие дополнительные аргументы не передаются автоматически. Вы получаете то, что передали функции.

Подробнее ...

обычный метод

«Стандартный» метод, как и во всех объектно-ориентированных языках. Когда вызывается метод объекта, ему автоматически предоставляется дополнительный аргумент self в качестве его первого аргумента. То есть метод

def f(self, x, y)

должен вызываться с двумя аргументами. self передается автоматически, и это сам объект . Подобно this, который волшебным образом появляется, например, в. java / c ++, только в python это отображается явно.

метод класса

Когда метод декорирован

@classmethod
def f(cls, x, y)

автоматически предоставленный аргумент не self, а класс self.

статический метод

Когда метод декорирован

@staticmethod
def f(x, y)

метод не получает никаких автоматических аргументов. Ему заданы только параметры, с которыми он вызывается.

использования

  • classmethod в основном используется для альтернативных конструкторов.
  • staticmethod не использует состояние объекта или даже структуру самого класса. Это может быть функция, внешняя по отношению к классу. Он помещается внутрь класса только для группировки функций с аналогичной функциональностью (например, таких как статические методы класса Java Math)
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)

avatar
David Schumann
24 января 2019 в 12:56
7

Вы можете рассмотреть разницу между:

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.

avatar
Michael Swartz
25 июня 2018 в 02:38
5

Мой вклад демонстрирует разницу между @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)
'''
"""
avatar
Selva
14 января 2018 в 14:07
30

Позвольте мне сначала рассказать о сходстве между методом, украшенным @classmethod, и @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',)
avatar
AbstProcDo
12 декабря 2017 в 09:40
3

Анализируйте @staticmethod буквально , предоставляя различные идеи.

Обычный метод класса - это неявный динамический метод , который принимает экземпляр в качестве первого аргумента.
Напротив, статический метод не принимает экземпляр в качестве первого аргумента, поэтому он называется 'static' .

Статический метод действительно является такой же нормальной функцией, как и функции вне определения класса.
К счастью, он сгруппирован в класс только для того, чтобы стоять поближе там, где он применяется, или вы можете прокрутить его, чтобы найти.

avatar
Tushar Vazirani
1 декабря 2017 в 10:38
2

Методы класса, как следует из названия, используются для внесения изменений в классы, а не в объекты. Чтобы внести изменения в классы, они изменят атрибуты класса (а не атрибуты объекта), поскольку именно так вы обновляете классы. Это причина того, что методы класса принимают класс (обычно обозначаемый как 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."
joel
13 августа 2020 в 21:19
0

методы не всегда вносят изменения

avatar
Gaurang Shah
10 октября 2017 в 10:10
52

Я начал изучать язык программирования с C ++, затем с Java, а затем с Python, поэтому этот вопрос меня тоже очень беспокоил, пока я не понял простое использование каждого из них.

Метод класса: Python, в отличие от Java и C ++, не имеет перегрузки конструктора. И для этого вы можете использовать classmethod. Следующий пример объяснит это

Предположим, у нас есть класс Person, который принимает два аргумента first_name и last_name и создает экземпляр Person.

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

Теперь, если возникает требование, когда вам нужно создать класс, используя только одно имя, только first_name, вы не можете сделать что-то подобное в Python.

Это приведет к ошибке, когда вы попытаетесь создать объект (экземпляр).

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __init__(self, first_name):
        self.first_name = first_name

Однако вы можете добиться того же, используя @classmethod, как указано ниже

class Person(object):

    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @classmethod
    def get_person(cls, first_name):
        return cls(first_name, "")

Статический метод: Это довольно просто, он не привязан к экземпляру или классу, и вы можете просто вызвать его, используя имя класса.

Итак, скажем, в приведенном выше примере вам нужна проверка того, что first_name не должен превышать 20 символов, вы можете просто сделать это.

@staticmethod  
def validate_name(name):
    return len(name) <= 20

, и вы можете просто позвонить, используя class name

Person.validate_name("Gaurang Shah")
akarilimano
17 сентября 2019 в 22:08
7

Это старый пост, но более питонический способ добиться, чтобы конструктор принимал один или два аргумента, использовал бы def __init__(self, first_name, last_name="") вместо classmethod get_person. И в этом случае результат будет точно таким же.

avatar
vijay
20 сентября 2017 в 16:57
7

@classmethod: может использоваться для создания общего глобального доступа ко всем экземплярам, ​​созданным этого класса ... например, для обновления записи несколькими пользователями .... Я особенно нашел это полезным при создании синглтонов .. :)

@static method: не имеет ничего общего с классом или экземпляром, связанным с ... но для удобства чтения можно использовать статический метод

avatar
Laxmi
3 октября 2016 в 10:41
37

Статические методы:

  • Простые функции без аргументов self.
  • Работа с атрибутами класса; не в атрибутах экземпляра.
  • Может вызываться как через класс, так и через экземпляр.
  • Для их создания используется встроенная функция staticmethod ().

Преимущества статических методов:

  • Он локализует имя функции в области классов
  • Он перемещает код функции ближе к тому месту, где он используется
  • Более удобный импорт по сравнению с функциями уровня модуля, так как каждый метод не нужно специально импортировать

    @staticmethod
    def some_static_method(*args, **kwds):
        pass
    

Методы класса:

  • Функции с первым аргументом в качестве имени класса.
  • Может вызываться как через класс, так и через экземпляр.
  • Они создаются с помощью встроенной функции classmethod.

     @classmethod
     def some_class_method(cls, *args, **kwds):
         pass
    
avatar
Adam Parkin
29 сентября 2016 в 17:02
19

Еще одно соображение относительно статического метода и метода классов связано с наследованием. Допустим, у вас есть следующий класс:

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

Также: историческое примечание: Гвидо Ван Россум (создатель Python) однажды назвал staticmethod «несчастным случаем»: https://mail.python.org/pipermail/python-ideas/2012- May / 014969.html

мы все знаем, насколько ограничены статические методы. (По сути, это случайность - еще во времена Python 2.2, когда я изобретал классы и дескрипторы нового стиля, я хотел реализовать методы класса, но сначала я не понял их и сначала случайно реализовал статические методы. было слишком поздно удалять их и предоставлять только методы класса.

Также: https://mail.python.org/pipermail/python-ideas/2016-July/041189.html

Честно говоря, статический метод был чем-то вроде ошибки - я пытался сделать что-то вроде методов класса Java, но как только он был выпущен, я обнаружил, что действительно был нужен метод классов. Но было слишком поздно избавляться от статического метода.

avatar
Rizwan Mumtaz
20 сентября 2016 в 09:03
9

Я постараюсь объяснить основное отличие на примере.

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- Метод класса привязан не к объекту, а к классу.

mirek
2 марта 2020 в 16:31
1

объявление 2: Вы уверены? Как статический метод мог вызвать метод класса? У него нет ссылки на него (на его класс).

avatar
Du D.
22 апреля 2016 в 15:40
85

Чтобы решить, использовать ли @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
Robert Nowak
11 октября 2019 в 10:38
0

в чем будет преимущество classmethod и cls._counter перед staticmethod и Apple._counter

apaderno
12 ноября 2019 в 22:55
2

cls._counter все равно будет cls._counter, даже если код будет помещен в другой класс или имя класса будет изменено. Apple._counter относится к классу Apple; для другого класса или при изменении имени класса вам потребуется изменить класс, на который указывает ссылка.

avatar
zangw
16 ноября 2015 в 02:00
23

Исчерпывающее руководство по использованию статических, классовых или абстрактных методов в Python является хорошей ссылкой для этой темы, и кратко изложите ее следующим образом.

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

  • Python не обязательно создавать экземпляр связанного метода для объекта.
  • Это облегчает читаемость кода и не зависит от состояния самого объекта;

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

  • Заводские методы , которые используются для создания экземпляра класса с использованием, например, некоторой предварительной обработки.
  • Статические методы вызывают статические методы : если вы разделяете статические методы на несколько статических методов, вам не следует жестко кодировать имя класса, а использовать методы класса
overflower
2 мая 2018 в 03:38
0

Спасибо @zangw - унаследованная неизменяемость статической функции, кажется, является ключевым отличием

avatar
Nathan Tregillus
19 мая 2015 в 15:27
40

Думаю, лучший вопрос: «Когда вы бы использовали @classmethod против @staticmethod

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

@staticmethod обеспечивает незначительный прирост производительности, но мне еще предстоит увидеть продуктивное использование статического метода в классе, которое не может быть реализовано как отдельная функция вне класса.

joel
13 августа 2020 в 21:37
0

В этом вопросе упоминается доступ частных членов класса. Я хочу подчеркнуть, что (в зависимости от вашего определения частного), @staticmethod имеют другой уровень доступа, чем @classmethod. Первый не должен обращаться к частным членам класса класса

avatar
Aaron Hall
23 января 2015 в 20:01
64

В чем разница между @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'>

См. исходный код pandas для других аналогичных примеров альтернативных конструкторов, а также см. Официальную документацию Python на classmethod и <2106265321023>.

avatar
Cathal Garvey
17 декабря 2012 в 18:51
-4

Быстрый взлом других идентичных методов в iPython показывает, что @staticmethod дает незначительный прирост производительности (в наносекундах), но в остальном, похоже, не выполняет никакой функции. Кроме того, любой прирост производительности, вероятно, будет сведен на нет дополнительной работой по обработке метода через staticmethod() во время компиляции (что происходит до выполнения любого кода при запуске сценария).

Ради удобочитаемости я бы не стал использовать @staticmethod, если только ваш метод не будет использоваться для большого количества работы, где учитываются наносекунды.

Keith Pinson
17 декабря 2012 в 19:46
7

«В противном случае, похоже, не выполняет никакой функции»: не совсем верно. См. Обсуждение выше.

avatar
Jens Timmerman
24 февраля 2012 в 09:32
34

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

avatar
Chris B.
3 ноября 2009 в 19:23
116

Официальная документация Python:

@classmethod

Метод класса получает класс как неявный первый аргумент, как и метод экземпляра получает экземпляр. Чтобы объявить метод класса, используйте это идиома:

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ... 

Форма @classmethod - это функция decorator - см. Описание определения функций в Функция определения для подробностей.

Его можно вызвать либо в классе (например, C.f()) или в экземпляре (например, C().f()). Экземпляр игнорируется, за исключением своего класса. Если метод класса вызывается для производного class, объект производного класса передается как подразумеваемый первый аргумент.

Методы класса отличаются от методов C ++ или статические методы Java. Если хочешь те, см. staticmethod() в этом раздел.

@staticmethod

Статический метод не получает неявный первый аргумент. Объявить статический метод, используйте эту идиому:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ... 

Форма @staticmethod - это функция decorator - см. Описание определения функций в Функция Определения для подробностей.

Его можно вызвать либо в классе (например, C.f()) или в экземпляре (например, C().f()). Экземпляр игнорируется, за исключением своего класса.

Статические методы в Python аналогичны к тем, что есть в Java или C ++. Для более продвинутая концепция, см. classmethod() в этом разделе.

mirek
2 марта 2020 в 15:33
0

В документах нет ошибки? Не должно быть статического метода: «И экземпляр, и его класс игнорируются». вместо «Экземпляр игнорируется, кроме его класса»?

Aaron Bentley
15 мая 2020 в 01:54
0

Это может быть ошибка вырезания и вставки, но, строго говоря, вы не можете вызвать метод класса, если игнорируете класс.

avatar
Tom Neyland
3 ноября 2009 в 19:02
90

Здесь короткая статья по этому вопросу

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

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

Mohan Gulati
3 ноября 2009 в 19:06
2

Значит ли это, что с помощью статического метода я всегда привязан к родительскому классу, а с помощью метода класса я привязан к классу, в котором я объявляю метод класса (в данном случае подкласс)?

Matt Anderson
3 ноября 2009 в 19:18
8

Нет. Используя статический метод, вы совсем не связаны; неявный первый параметр отсутствует. Используя метод classmethod, вы получаете в качестве неявного первого параметра класс, для которого вы вызвали метод (если вы вызвали его непосредственно для класса), или класс экземпляра, для которого вы вызвали метод (если вы вызвали его для экземпляра).

MestreLion
3 мая 2012 в 10:01
8

Можно немного расширить, чтобы показать, что, имея класс в качестве первого аргумента, методы класса имеют прямой доступ к другим атрибутам и методам класса, а статические методы - нет (для этого им потребуется жестко запрограммировать MyClass.attr)

c z
17 сентября 2019 в 15:20
1

«Это определение неизменяемо через наследование». не имеет смысла в Python, вы можете просто переопределить статический метод.

avatar
Armin Ronacher
25 сентября 2008 в 21:24
25

@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'>
Aaron Hall♦
14 июня 2016 в 21:57
1

Один из возможных недостатков метакласса, который сразу же приходит мне в голову, заключается в том, что вы не можете вызвать метод класса непосредственно в экземпляре. c = C(); c.foo() вызывает AttributeError, вам нужно будет сделать type(c).foo(). Это также можно считать функцией - хотя я не могу понять, зачем вам это нужно.

avatar
Terence Simpson
25 сентября 2008 в 21:07
171

Обычно @classmethod создает метод, первым аргументом которого является класс, из которого он вызывается (а не экземпляр класса), @staticmethod не имеет никаких неявных аргументов.

avatar
Thomas Wouters
25 сентября 2008 в 21:05
865

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
>>> 
Tony Meyer
26 сентября 2008 в 10:10
767

Статический метод не бесполезен - это способ поместить функцию в класс (потому что она логически принадлежит ему), указывая при этом, что он не требует доступа к классу.

Thomas Wouters
26 сентября 2008 в 13:40
148

Отсюда только «принципиально» бесполезно. Такая организация, а также внедрение зависимостей являются допустимым использованием статических методов, но поскольку модули, а не классы, как в Java, являются основными элементами организации кода в Python, их использование и полезность редки.

Ben James
12 марта 2010 в 09:11
44

Что логично в определении метода внутри класса, если он не имеет ничего общего ни с классом, ни с его экземплярами?

haridsv
8 апреля 2010 в 01:32
114

Может, ради наследства? Статические методы можно наследовать и переопределять так же, как методы экземпляра и методы класса, и поиск работает должным образом (в отличие от Java). Статические методы на самом деле не разрешаются статически независимо от того, вызываются ли они в классе или экземпляре, поэтому единственное различие между классовыми и статическими методами - это неявный первый аргумент.

Imbrondir
22 августа 2011 в 11:58
87

Они также создают более чистое пространство имен и упрощают понимание того, что функция как-то связана с классом.

Ethan Furman
8 ноября 2011 в 19:50
18

@BenJames: статический метод должен иметь какое-то отношение к классу, в котором он определен, ему просто не нужны ни класс, ни данные экземпляра для выполнения своей работы. (см. coderhelper.com/questions/7855237)

Adam Parkin
2 марта 2012 в 23:17
7

Итак, что лучше: наличие класса Math с такими методами, как sin, cos и т. Д., Или наличие модуля Math с функциями sin, cos и т. Д. Мне кажется, что последний будет быть предпочтительнее, поэтому мне трудно думать о возможном случае, когда staticmethod был бы полезен. Я полагаю, что вспомогательные методы - это потенциальный случай, но если помощник настолько универсален, что ему не нужно ничего из класса или экземпляра класса, вам действительно нужно задаться вопросом, не будет ли он лучше служить служебной программой в библиотеке.

Jonathan
13 апреля 2012 в 16:04
5

Когда у вас есть экземпляр класса, его часто проще вызвать inst.method(), чем module.method(). Иногда вы даже не знаете, какой модуль вы используете, Python в конце концов - это динамический язык ...

MestreLion
3 мая 2012 в 09:40
21

-1: Этот ответ A) дает очень важную особенность метода класса в качестве объяснения его полезности (поэтому они полезны только при создании подкласса, чтобы дать вам имя подкласса, действительно ?), и B) подразумевает, что «статические методы должны быть модульными методами», что, хотя они семантически таковы, имеет смысл лот или имеет смысл использовать их , если ваш модуль определяет много классов и заданную вспомогательную функцию. применяется только к данному классу . Так что очень плохой ответ с большим количеством заблуждений imho.