tutorial - ¿Cómo capturar cualquier método llamado en un objeto en python?
framework django para python (2)
Esta es una manera de hacerlo.
import inspect
from functools import wraps
from collections import namedtuple
call = namedtuple(''Call'', [''fname'', ''args'', ''kwargs''])
calls = []
def register_calls(f):
@wraps(f)
def f_call(*args, **kw):
calls.append(call(f.__name__, args, kw))
print calls
return f(*args, **kw)
return f_call
def decorate_methods(decorator):
def class_decorator(cls):
for name, m in inspect.getmembers(cls, inspect.ismethod):
setattr(cls, name, decorator(m))
return cls
return class_decorator
@decorate_methods(register_calls)
class Test(object):
def test1(self):
print ''test1''
def test2(self):
print ''test2''
Ahora todas las llamadas a test1
y test2
se registrarán en la list
calls
.
decorate_methods
aplica un decorador a cada método de la clase. register_calls
registra las llamadas a los métodos en calls
, con el nombre de la función y los argumentos.
Estoy buscando una solución pythonic sobre cómo almacenar un método que se llama en un objeto justo dentro del objeto.
Porque en Python, si quiero capturar, por ejemplo, el método abs()
, sobrecargaré este operador como:
Catcher(object):
def __abs__(self):
self.function = abs
c = Catcher()
abs(c) # Now c.function stores ''abs'' as it was called on c
Si quiero capturar una función, que tiene otro atributo, por ejemplo pow()
, voy a usar esto:
Catcher(object):
def __pow__(self, value):
self.function = pow
self.value = value
c = Catcher()
c ** 2 # Now c.function stores ''pow'', and c.value stores ''2''
Ahora, lo que estoy buscando es una solución general, para capturar y almacenar cualquier tipo de función llamada en Catcher
, sin implementar todas las sobrecargas y otros casos. Y como puede ver, también quiero almacenar los valores (¿ quizás en una lista, si hay más de uno de ellos? ) Que son los atributos de un método.
¡Gracias por adelantado!
Una metaclase no ayudará aquí; aunque se buscan métodos especiales en el tipo del objeto actual (por lo tanto, la clase para las instancias), no se consulta __getattribute__
o __getattr__
al hacerlo (probablemente porque son métodos especiales en sí mismos). Entonces, para atrapar todos los métodos de dunder, estás obligado a crearlos todos.
Puede obtener una lista bastante decente de todos los métodos especiales del operador ( __pow__
, __gt__
, etc.) enumerando el módulo del operator
:
import operator
operator_hooks = [name for name in dir(operator) if name.startswith(''__'') and name.endswith(''__'')]
Armado con esa lista, un decorador de clase podría ser:
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
Luego aplícalo a tu clase:
@instrument_operator_hooks
class CatchAll(object):
pass
Manifestación:
>>> 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,), {})
Entonces, aunque nuestra clase no define __pow__
explícitamente, todavía estamos enganchados a ella.