Согласно модели данных Python, язык предлагает три вида методов: статические, класса и экземпляра класса. Давайте посмотрим, что же происходит за кулисами каждого из видов методов. Понимание принципов их работы поможет в создании красивого и эффективного кода. Начнём с самого простого примера, в котором демонстрируются все три вида методов.
class ToyClass:
def instancemethod(self):
return 'instance method called', self
@classmethod
def classmethod(cls):
return 'class method called', cls
@staticmethod
def staticmethod():
return 'static method called'
Это наиболее часто используемый вид методов. Методы экземпляра класса принимают объект класса как первый аргумент, который принято называть self
и который указывает на сам экземпляр. Количество параметров метода не ограничено.
Используя параметр self
, мы можем менять состояние объекта и обращаться к другим его методам и параметрам. К тому же, используя атрибут self.__class__
, мы получаем доступ к атрибутам класса и возможности менять состояние самого класса. То есть методы экземпляров класса позволяют менять как состояние определённого объекта, так и класса.
Встроенный пример метода экземпляра — str.upper()
:
Методы класса принимают класс в качестве параметра, который принято обозначать как cls
. Он указывает на класс ToyClass, а не на объект этого класса. При декларации методов этого вида используется декоратор classmethod
.
Методы класса привязаны к самому классу, а не его экземпляру. Они могут менять состояние класса, что отразится на всех объектах этого класса, но не могут менять конкретный объект.
Встроенный пример метода класса — dict.fromkeys()
— возвращает новый словарь с переданными элементами в качестве ключей.
Статические методы декларируются при помощи декоратора staticmethod
. Им не нужен определённый первый аргумент (ни self
, ни cls
).
Их можно воспринимать как методы, которые “не знают, к какому классу относятся”.
Таким образом, статические методы прикреплены к классу лишь для удобства и не могут менять состояние ни класса, ни его экземпляра.
С теорией достаточно. Давайте разберёмся с работой методов, создав объект нашего класса и вызвав поочерёдно каждый из методов: instancemethod, classmethod and staticmethod.
>>> obj = ToyClass()
>>> obj.instancemethod()
('instance method called', ToyClass instance at 0x10f47e7a0>)
>>> ToyClass.instancemethod(obj)
('instance method called', ToyClass instance at 0x10f47e7a0>)
Пример выше подтверждает то, что метод instancemethod имеет доступ к объекту класса ToyClass через аргумент self
. Кстати, вызов функции obj.instancemethod()
используется лишь для удобства, то есть можно использовать и ToyClass.instancemethod(obj)
.
Теперь давайте вызовем метод класса:
>>> obj.classmethod()('class method called', <class ToyClass at 0x10f453a10>)Мы видим, что метод класса classmethod() имеет доступ к самому классу ToyClass, но не к его конкретному экземпляру объекта. Запомните, в Python всё является объектом. Класс тоже объект, который мы можем передать функции в качестве аргумента.
Заметьте, что self
и cls
— не обязательные названия и эти параметры можно называть иначе.
def instancemethod(self, ...)
def classmethod(cls, ...)
-------то же самое, что и----------
def instancemethod(my_object, ...)
def classmethod(my_class, ...)
Это лишь общепринятые обозначения, которым следуют все. Тем не менее они должны находиться первыми в списке параметров.
Вызовем статический метод:
>>> obj.staticmethod()static method calledДа, это может вас удивить, но статические методы можно вызывать через объект класса. Вызов через точку нужен лишь для удобства. На самом же деле в случае статического метода никакие аргументы (self
илиcls
) методу не передаются.
То есть статические методы не могут получить доступ к параметрам класса или объекта. Они работают только с теми данными, которые им передаются в качестве аргументов.
Теперь давайте вызовем те же самые методы, но на самом классе.
>>> ToyClass.classmethod()
('class method called', <class ToyClass at 0x10f453a10>)
>>> ToyClass.staticmethod()
'static method called'
>>> ToyClass.instancemethod()
TypeError: unbound method instancemethod()
must be called with ToyClass instance as
first argument (got nothing instead)
Метод класса и статический метод работают, как нужно. Однако вызов метода экземпляра класса выдаёт TypeError, так как метод не может получить на вход экземпляр класса.
Теперь, когда вы знаете разницу между тремя видами методов, давайте рассмотрим реальный пример для понимания того, когда и какой метод стоит использовать. Пример взят отсюда.
from datetime import date
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
@classmethod
def from_birth_year(cls, name, year):
return cls(name, date.today().year - year)
@staticmethod
def is_adult(age):
return age > 18
person1 = Person('Sarah', 25)
person2 = Person.from_birth_year('Roark', 1994)
>>> person1.name, person1.age
Sarah 25
>>> person2.name, person2.age
Roark 24
>>> Person.is_adult(25)
True
Выбор того, какой из методов использовать, может показаться достаточно сложным. Тем не менее с опытом этот выбор делать гораздо проще.
Чаще всего метод класса используется тогда, когда нужен генерирующий метод, возвращающий объект класса. Как видим, метод класса from_birth_year
используется для создания объекта класса Person
по году рождения, а не возрасту.
Статические методы в основном используются как вспомогательные функции и работают с данными, которые им передаются.
self
и к классу через self.__class__
.cls
.Перевод статьи Aaron (Ari) Bornstein: AI Search Algorithms Every Data Scientist Should Know
Комментарии