Спеціальний метод пошуку

Оновлено: 28.04.2023

Для користувальницьких класів неявні виклики спеціальних методів гарантовано працюють правильно, лише якщо вони визначені для типу об’єкта, а не в словнику екземплярів об’єкта. Така поведінка є причиною того, що наступний код викликає виняток:

>>> class C:
...     pass
...
>>> c = C()
>>> c.__len__ = lambda: 5
>>> len(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'C' has no len()

Обґрунтування такої поведінки полягає в ряді спеціальних методів, таких як __hash__() і __repr__(), які реалізуються всіма об’єктами, включаючи об’єкти типу. Якби неявний пошук цих методів використовував звичайний процес пошуку, вони зазнали б помилки під час виклику в самому об’єкті типу:

>>> 1 .__hash__() == hash(1)
True
>>> int.__hash__() == hash(int)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor '__hash__' of 'int' object needs an argument

Неправильна спроба викликати незв’язаний метод класу таким чином іноді називається «плутаниною метакласу», і її можна уникнути шляхом обходу екземпляра під час пошуку спеціальних методів:

>>> type(1).__hash__(1) == hash(1)
True
>>> type(int).__hash__(int) == hash(int)
True

Окрім обходу будь-яких атрибутів екземпляра в інтересах коректності, неявний пошук спеціального методу зазвичай також обходить метод __getattribute__() навіть метакласу об’єкта:

>>> class Meta(type):
...     def __getattribute__(*args):
...         print("Metaclass getattribute invoked")
...         return type.__getattribute__(*args)
...
>>> class C(object, metaclass=Meta):
...     def __len__(self):
...         return 10
...     def __getattribute__(*args):
...         print("Class getattribute invoked")
...         return object.__getattribute__(*args)
...
>>> c = C()
>>> c.__len__()                 # Explicit lookup via instance
Class getattribute invoked
10
>>> type(c).__len__(c)          # Explicit lookup via type
Metaclass getattribute invoked
10
>>> len(c)                      # Implicit lookup
10

Обхід механізму __getattribute__() у такий спосіб забезпечує значні можливості для оптимізації швидкості в інтерпретаторі за рахунок певної гнучкості в обробці спеціальних методів (спеціальний метод має бути встановлений на об’єкті класу сам для того, щоб бути послідовно викликаним інтерпретатором).