Перейти до змісту

Статичні та класові методи

Згідно моделі даних, Python пропонує три види методів: статичні, класові і екземпляра класа.

Методи екземпляра класа

Це вже знайомий нам вид методів. Методи екземпляра класа приймають об'єкт класа як перший аргумент, який прийнято називати self і який вказує на сам екземпляр.

Використовуючи параметр self ми можемо міняти стан об'єкта і звертатись до інших його методів і параметрів. Також через атрибут self.__class__ можна отримати доступ до атрибутів класа і можливість міняти стан самого класа. Тобто методи екземплярів класа дозволяють міняти як стан певного об'єкта, так і класа.

Класові методи

Методи класа приймають клас в якості параметра, його прийнято позначати як cls. Він вказує на клас, а не на об'єкт цього класа. При декларації методів цього вида використовують наступний синтаксис:

>>> class A:
...     @classmethod
...     def f(cls):
...             pass
...
>>> A.f
<bound method A.f of <class '__main__.A'>>
>>>

Методи класа прив'язані до самого класа, а не його екземпляра. Вони можуть міняти стан класа, що відобразиться на усіх об'єктах цього класа, але не можуть міняти конкретний об`єкт.

Приклад метода класа — dict.fromkeys() — повертає новий словник з переданими елементами в якості ключів:

>>> dict.fromkeys('abc')
{'a': None, 'b': None, 'c': None}
>>>

Статичні методи

Статичні методи декларуються за допомогою наступного синтаксиса:

>>> class A:
...     @staticmethod
...     def f():
...             pass
...
>>> A.f
<function A.f at 0x0000028FECA8C1F0>
>>>

Їх можна сприймати як методи, які "не знають, до якого класа відносяться".

Таким чином, статичні методи прикріплені до класа лише для зручності і не можуть міняти стан ні класа, ні його екземпляра.

До практики

Напишемо клас, де використовуються усі три види методів.

>>> class ToyClass:
...     def instance_method(self):
...         return 'instance method called', self
...     @classmethod
...     def class_method(cls):
...         return 'class method called', cls
...     @staticmethod
...     def static_method():
...         return 'static method called'
...
>>> obj = ToyClass()
>>>

Розберемось з роботиою методів. Спочатку метод екземпляра:

>>> obj.instance_method()
('instance method called', <__main__.ToyClass object at 0x00000179B2FF4CA0>)
>>>

Приклад вище підтверджує те, що метод instance_method має доступ до обєкта класаToyClassчерез аргументself. Зауважте: методobj.instance_method()` можна викоикати і так:

ToyClass.instance_method(obj)

Тепер скористаємось методом класа:

>>> obj.class_method()
('class method called', <class '__main__.ToyClass'>)
>>>

Як видно, метод класа class_method() має доступ до самого класа ToyClass, але не до його конкретного екземпляра. Пам'ятаєте що в Python усе є об'єкт? Клас — теж об'єкт, який ми можемо передати функції в якості аргумента.

Викликаємо статичний метод:

>>> obj.static_method()
'static method called'
>>>

Це може здатися дивним, але статичні методи можна викликати через об'єкт класа. Насправді статичному методу ніякі спеціальні аргументи (self чи cls) не передаються. Тобто статичні методи не можуть отримати доступ до параметрів класа чи об'єкта. Вони працюють тільки з тими даними, які їм передаються як аргументи.

Тепер давайте викличемо ті ж самі методи, але на самому класі.

>>> ToyClass.class_method()
('class method called', <class '__main__.ToyClass'>)
>>>
>>> ToyClass.instance_method()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: instance_method() missing 1 required positional argument: 'self'
>>>

Виклик метода екземпляра класа видає TypeError. Сталося це через те, що метод очікував екземпляр класа, а було передано клас.

Зі статичним методом нічого неочікуваного:

>>> ToyClass.static_method()
'static method called'
>>>

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

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

Спробуємо створити об'єкт:

>>> p = Person('Jane', 25)
>>> p.age
25
>>>

Тепер викликаємо метод класа, який у свою чергу створить об'єкт цього ж класа і поверне його:

>>> p = Person.from_birth_year('John', 1989)
>>> p
<__main__.Person object at 0x000001D1A0D6ACA0>
>>> p.age
30
>>>

І, нарешті, статичний метод класа Person:

>>> Person.is_adult(25)
True
>>>

Практики використання

Вибір того, який з методів використовувати, може здатись доволі складним. З набутим досвідом цей вибір зробити буде простіше.

Найчастіше метод класа використовується тоді, коли потрібен генеруючий метод, який повертає об'єкт класа. Статичні методи в основному використовуються як допоміжні функції і працюють з даними, які їм передаються.

Резюме

  • Методи екземпляра класа отримують доступ до об'єкт класа через параметр self і до класа через self.__class__
  • Методи класа не можуть отримати доступ до певного екземпляра класа, але мають доступ до самого класа через cls
  • Статичні методи працюють як звичайні функції, але належать до простору імен класа. Вони не мають доступа ні до самого класа, ні до його екземплярів
Back to top