Ответ 1
Метаклас не поможет здесь; хотя специальные методы просматриваются по типу текущего объекта (поэтому класс для экземпляров), __getattribute__
или __getattr__
не рассматриваются при этом (вероятно, потому, что они сами являются специальными методами). Итак, чтобы поймать все методы dunder, вы вынуждены создавать их все.
Вы можете получить довольно приличный список всех специальных методов оператора (__pow__
, __gt__
и т.д.), перечислив operator
module:
import operator
operator_hooks = [name for name in dir(operator) if name.startswith('__') and name.endswith('__')]
Вооруженный этим списком декоратор класса может быть:
def instrument_operator_hooks(cls):
def add_hook(name):
operator_func = getattr(operator, name.strip('_'), None)
existing = getattr(cls, name, None)
def op_hook(self, *args, **kw):
print "Hooking into {}".format(name)
self._function = operator_func
self._params = (args, kw)
if existing is not None:
return existing(self, *args, **kw)
raise AttributeError(name)
try:
setattr(cls, name, op_hook)
except (AttributeError, TypeError):
pass # skip __name__ and __doc__ and the like
for hook_name in operator_hooks:
add_hook(hook_name)
return cls
Затем примените это к вашему классу:
@instrument_operator_hooks
class CatchAll(object):
pass
Демо:
>>> c = CatchAll()
>>> c ** 2
Hooking into __pow__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 11, in op_hook
AttributeError: __pow__
>>> c._function
<built-in function pow>
>>> c._params
((2,), {})
Итак, хотя наш класс явно не определяет __pow__
, мы все еще подключаемся к нему.