Магический метод — это просто метод, который вызывается неявно, часто чтобы выполнить операцию для типа. К примеру, можно определить __bool__ метод, чтобы указать является ли объект True/False в булевом контексте (если поведение по умолчанию, основанное на __len__ или True, не подходит). Очень многие аспекты поведения объектов в Питоне могут быть изменены таким образом. К примеру, plumbum модуль, за счёт перегрузки операторов, встраивает sh-подобный язык прямо в Питон:
#!/usr/bin/env python
from plumbum.cmd import awk, sort
(awk["-f", "script.awk"] << "input data" | sort > "outfile.txt")()
Здесь определены __getitem__([]), __lshift__(<<), __or__(|), __gt__(>), __call__(()) методы.
Как реализована архитектура магических методов в python?
Как происходит определение вызова нужного метода нужного класса?
repr(SomeClass()) приводит к вызову SomeClass.__repr__ (если определён), потому что документация repr() функции так говорит.
В целом, ничего особенно не происходит, к примеру, вы можете свой протокол определить:
def quack(obj):
return getattr(obj, 'quack', None)()
Любой объект, для которого определён метод quack(), независимо от его базовых классов, можно передать в функцию quack():
class Duck:
def quack(self):
return "Quack! Quack! Quack!"
print(quack(Duck()))
Для остальных классов TypeError выбрасывается:
try:
quack(1)
except TypeError:
print("целое число не квакает")
Отличие quack() от настоящих магических методов, что не используется двойное подчёркивание в имени метода, по соглашению, зарезервированное для протоколов, определяемых самим языком и нет требования определить метод в самом классе (определения из Мета-класса или присваивание метода объекту напрямую могут не работать для магических методов):
>>> class C:
... pass
>>> c = C()
>>> c.__len__ = lambda: 0
>>> len(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'C' has no len()
Для сравнения, quack() можно прямо самому объекту присвоить:
>>> c.quack = lambda: "ga ga"
>>> quack(c)
'ga ga'
Но чтобы len() работала, необходимо определение в сам класс помещать:
>>> class C:
... def __len__(self):
... return 0
>>> len(C())
0
Это сделано для оптимизации скорости вызова подобных методов и чтобы избежать "metaclass confusion", описанную выше по ссылке. В CPython, почти все специальные методы (термин для магических методов из спецификации языка) хранятся в специальных слотах в типе.
return "({x})".format(x)должно бытьreturn "({})".format(self.x)– Xander Mar 30 '17 at 13:51