A metaclass won't help here; although special methods are looked up on the type of the current object (so the class for instances), __getattribute__
or __getattr__
are not consulted when doing so (probably because they are themselves special methods). So to catch all dunder methods, you are forced to create them all.
You can get a pretty decent list of all operator special methods (__pow__
, __gt__
, etc.) by enumerating the operator
module:
import operator
operator_hooks = [name for name in dir(operator) if name.startswith('__') and name.endswith('__')]
Armed with that list a class decorator could be:
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
Then apply that to your class:
@instrument_operator_hooks
class CatchAll(object):
pass
Demo:
>>> 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,), {})
So, even though our class doesn't define __pow__
explicitly, we still hooked into it.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…