Спеціальний метод пошуку
Оновлено: 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__() у такий спосіб забезпечує значні можливості для оптимізації швидкості в інтерпретаторі за рахунок певної гнучкості в обробці спеціальних методів (спеціальний метод має бути встановлений на об’єкті класу сам для того, щоб бути послідовно викликаним інтерпретатором).