Что такое «методы класса» и «методы экземпляра» в Python?

37

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

На мой взгляд, хотя терминология отличается в разных языках, мы можем классифицировать функции следующим образом:

  • [бесплатные] функции
  • статические методы / статические функции-члены
  • нестатические методы / нестатические функции-члены

По-видимому, в Python есть функция другого типа, которая не вписывается в вышеупомянутые категории, та, которая является методом, но «не знает своего класса».

Что такое "методы класса" и "методы экземпляра" в Python?

Гонки легкости с Моникой
источник
1
Если кто-то заинтересован в обсуждении чата, начните здесь: chat.stackexchange.com/transcript/message/26479002#26479002
Яннис
1
ИМО лучший ответ дает другой веб-сайт stackexchange: stackoverflow.com/questions/136097/…
Тревор Бойд Смит
К вашему сведению, ссылка предоставлена ​​внизу принятого ... но я думаю, что многие люди пропустят ссылку или никогда не получат ее, поэтому я скопировал ее здесь.
Тревор Бойд Смит

Ответы:

49

Краткий ответ

  • метод экземпляра знает свой экземпляр (и, следовательно, класс)
  • метод класса знает свой класс
  • статический метод не знает своего класса или экземпляра

Длинный ответ

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

Метод класса является тот , который принадлежит к классу в целом. Это не требует экземпляра. Вместо этого класс будет автоматически отправлен в качестве первого аргумента. Метод класса объявлен с @classmethodдекоратором.

Например:

class Foo(object):
    @classmethod
    def hello(cls):
        print("hello from %s" % cls.__name__)
Foo.hello()
-> "Hello from Foo"
Foo().hello()
-> "Hello from Foo"

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

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

class Foo(object):
    def hello(self):
        print("hello from %s" % self.__class__.__name__)
Foo.hello()
-> TypeError: hello() missing 1 required positional argument: 'self'

(примечание: выше с python3; с python2 вы получите немного другую ошибку)

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

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

class Foo(object):
    @staticmethod
    def hello(cls):
        print("hello from %s" % cls.__name__)

Foo.hello()
-> TypeError: hello() missing 1 required positional argument: 'cls'

Ссылки на документацию

Вот ссылки на соответствующий документ python3:

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

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

Объекты метода класса Объект метода класса, подобно объекту статического метода, является оберткой вокруг другого объекта, которая изменяет способ извлечения этого объекта из классов и экземпляров классов. Поведение объектов метода класса при таком извлечении описано выше в разделе «Пользовательские методы». Объекты метода класса создаются встроенным конструктором classmethod ().

Смежные вопросы

Брайан Оукли
источник
4
@LightnessRacesinOrbit: Я не думаю, что статические методы особенно часто используются в python. Используя мою систему в качестве случайной выборки, при поиске во всех установленных мной пакетах только около 1% всех методов являются статическими (что, честно говоря, оказалось больше, чем я ожидал).
Брайан Оукли
1
«Статические методы» обычно являются запахом кода и не приветствуются, например, в руководстве по стилю Gogole Python.
9000
3
Я думаю, что ответ был бы лучше, если бы он обратил внимание на подклассы, где методы классов получают некоторую полезность.
Уинстон Эверт
1
@ user2357112: Я просто посчитал все экземпляры "^ def(", затем пересчитал все экземпляры @staticmethod. Очень ненаучно, но, вероятно, было в рамках.
Брайан Оукли
2
@Kashyap: TL; DR относится к самому первому абзацу. Я думаю, что это работает лучше наверху, чем внизу.
Брайан Окли
8

Что такое «методы класса» и «методы экземпляра» в Python?

  • «Метод экземпляра» использует информацию, содержащуюся в экземпляре, чтобы выяснить, какое значение вернуть (или какой побочный эффект нужно сделать). Это очень распространено.

  • «Метод класса» использует информацию о классе (а не об экземпляре этого класса), чтобы повлиять на то, что он делает (они обычно используются для создания новых экземпляров в качестве альтернативных конструкторов и, следовательно, не являются невероятно распространенными).

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

Функция Х

Помните математический класс, "у есть функция х f(x),?" Давайте применим это в коде:

y = function(x)

Под вышесказанным подразумевается, что поскольку xмогут меняться, yмогут меняться при xизменениях. Это то, что подразумевается, когда мы говорим, что " yявляется функцией x"

Что будет yкогда zесть 1? 2? 'FooBarBaz'?

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

Помните вышеупомянутое, читая следующие описания:

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

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

Встроенным примером метода экземпляра является str.lower:

>>> 'ABC'.lower()
'abc'

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

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

Помните, в Python все является объектом. Это означает, что класс является объектом и может быть передан в качестве аргумента функции.

Метод класса является функцией , которая является функцией от класса . Он принимает класс в качестве аргумента.

Встроенный пример dict.fromkeys:

>>> dict.fromkeys('ABC')
{'C': None, 'B': None, 'A': None}

Функция неявно знает свой собственный класс, функция использует этот класс для воздействия на выходные данные функции и создает новый из этого класса из итерируемого. OrderedDict демонстрирует это при использовании того же метода:

>>> from collections import OrderedDict
>>> OrderedDict.fromkeys('ABC')
OrderedDict([('A', None), ('B', None), ('C', None)])

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

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

Вы упоминаете метод, который «не знает своего класса» - это статический метод в Python. Это просто присоединено для удобства к объекту класса. При желании это может быть отдельная функция в другом модуле, но ее сигнатура вызова будет такой же.

Статический метод не является функцией ни класса, ни объекта.

Встроенным примером статического метода является str.maketrans из Python 3.

>>> str.maketrans('abc', 'bca')
{97: 98, 98: 99, 99: 97}

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

Это удобно, потому что strвсегда доступно в глобальном пространстве имен, поэтому вы можете легко использовать его с функцией перевода:

>>> 'abracadabra'.translate(str.maketrans('abc', 'bca'))
'bcrbabdbcrb'

В Python 2 вы должны получить к нему доступ из stringмодуля:

>>> 'abracadabra'.translate(str.maketrans('abc', 'bca'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: type object 'str' has no attribute 'maketrans'
>>> import string
>>> 'abracadabra'.translate(string.maketrans('abc', 'bca'))
'bcrbabdbcrb'

пример

class AClass(object):
    """In Python, a class may have several types of methods: 
    instance methods, class methods, and static methods
    """

    def an_instance_method(self, x, y, z=None):
        """this is a function of the instance of the object
        self is the object's instance
        """
        return self.a_class_method(x, y)

    @classmethod
    def a_class_method(cls, x, y, z=None):
        """this is a function of the class of the object
        cls is the object's class
        """
        return cls.a_static_method(x, y, z=z)

    @staticmethod
    def a_static_method(x, y, z=None):
        """this is neither a function of the instance or class to 
        which it is attached
        """
        return x, y, z

Давайте создадим:

>>> instance = AClass()

Теперь экземпляр может вызывать все методы:

>>> instance.an_instance_method('x', 'y')
('x', 'y', None)
>>> instance.a_static_method('x', 'y')
('x', 'y', None)
>>> instance.a_class_method('x', 'y')
('x', 'y', None)

Но обычно класс не предназначен для вызова метода экземпляра, хотя ожидается, что он вызовет другие:

>>> AClass.a_class_method('x', 'y')
('x', 'y', None)
>>> AClass.a_static_method('x', 'y')
('x', 'y', None)
>>> AClass.an_instance_method('x', 'y')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: an_instance_method() missing 1 required positional argument: 'y'

Вы должны были бы явно передать экземпляр для вызова метода экземпляра:

>>> AClass.an_instance_method(instance, 'x', 'y')
('x', 'y', None)
Аарон Холл
источник
«Это статический метод в Python. Он просто присоединен для удобства к объекту класса. Он может быть отдельной функцией в другом модуле, но его сигнатура вызова будет такой же». Кажется более запутанным, чем удобным. Зачем вам прикреплять функцию к классу, если его тело не имеет представления об этом классе.
Легкость гонки с Моникой
@LightnessRacesinOrbit Я подробно остановился на каждой точке
Аарон Холл
Лично я всегда смотрел на это так, что метод экземпляра - это операция над конкретным экземпляром, метод класса - это своего рода конструктор (для удобства ничего похожего, MyClass(1,2,3)а не как, MyClass.get_special(1,2,3)) и статический метод как просто быть нормальной функцией, которая в любом случае может стоять одна.
Zizouz212
Да, методы класса обычно используются для альтернативных конструкторов (см. Мой пример dict.fromkeys выше, см. Также исходный код pandas.DataFrame), но это не определяющая характеристика. Иногда мне просто нужно получить доступ к данным, хранящимся на уровне класса, из метода, не обращаясь к экземпляру. Если вы вызываете тип (self) (или, что еще хуже, self .__ class__) и не используете напрямую непосредственно метод self в методе экземпляра, это хороший признак того, что вы должны рассматривать его как метод класса. Поскольку методы класса не требуют экземпляров, это облегчает их юнит-тестирование.
Аарон Холл
4
  • «Метод экземпляра» - это метод, который передается объекту экземпляра в качестве первого параметра, который по соглашению вызывается self. Это по умолчанию.

  • «Статический метод» - это метод без начального selfпараметра. Это реализовано с помощью декоратора @staticmethod .

  • «Метод класса» - это метод, в котором начальный параметр является не объектом экземпляра, а объектом класса ( см. Эту часть документации по Python, что такое «объект класса»), который по соглашению называется cls. Это реализовано с помощью декоратора @classmethod .

Типичное использование термина «статический метод» в C / C ++ / etc относится как к методам Python «статический», так и к методам «класс» Python, так как оба этих типа методов не имеют ссылки на экземпляр. В C / C ++ / etc методы обычно имеют неявный доступ ко всем элементам / методам класса, не относящимся к экземпляру, поэтому, вероятно, такое различие существует только в таких языках, как Python / Ruby / etc.

Ixrec
источник
Есть ли у статического метода в C / C ++ ссылка на объект класса? Или просто «члены» класса? Могут ли они создавать новых членов класса? :)
Аарон Холл
3
@AaronHall В C / C ++ нет такого понятия, как объект класса, и вам не нужна ссылка на него для вызова статических методов. Вы просто пишете Class::staticMethod()и все (если вам не запрещено звонить, потому что staticMethod()это личное или что-то в этом роде).
Ixrec