Статичні та класові методи
Згідно моделі даних, 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
- Статичні методи працюють як звичайні функції, але належать до простору імен класа. Вони не мають доступа ні до самого класа, ні до його екземплярів