with tutorial para framework español develop applications python oop object methods call

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.