¿Cómo decorar un método de objeto?

Necesito decorar el método de un objeto. Debe estar en tiempo de ejecución porque los decoradores aplicados al objeto dependen de los argumentos que el usuario dio al llamar al programa (argumentos proporcionados con argv), por lo que un mismo objeto podría decorarse 3 veces, 2 veces o no decorarse en todos.

Aquí hay algo de contexto, el programa es un solucionador de acertijos, el comportamiento principal es encontrar una solución para el acertijo automáticamente, por automáticamente quiero decir sin la intervención del usuario. Y aquí es donde se pone en juego la decoración, una de las cosas que quiero es dibujar un gráfico de lo que pasó durante la ejecución, pero quiero hacerlo solo cuando la bandera --draw-graph se utiliza.

Esto es lo que he probado:

class GraphDecorator(object):
    def __init__(self, wrappee):
        self.wrappee = wrappee
    def method(self):
        # do my stuff here
        self.wrappee.method()
        # do more of stuff here
    def __getattr__(self,attr):
        return getattr(self.wrappee,attr)

Y por qué NO funcionó: No funcionó debido a la forma en que construí la aplicación, cuando se llamó a un método que no existía en mi clase Decorator, se sintió de nuevo a la implementación de la clase decorada, el problema es que la aplicación siempre comencé a invocar el método run que no necesitaba ser decorado, por lo que se utilizó el respaldo sin decorar y desde el interior de la forma sin decorar siempre se llamaba sin decorar métodos, lo que necesitaba era reemplazar el método del objeto, no usarlo como proxy:

# method responsible to replace the undecorated form by the decorated one
def graphDecorator(obj):
    old_method = obj.method

    def method(self):
        # do my stuff here
        old_method()
        # do more of my stuff

    setattr(obj,'method',method) # replace with the decorated form

Y aquí está mi problema, la forma decorada no recibe self cuando se llama resultando en un TypeError debido al número incorrecto de argumentos.

preguntado el 10 de mayo de 11 a las 13:05

Space_C0wb0y - Tengo un problema con __getattr__, la clase A es la clase decorada por la clase B, A tiene un método run () que llama al método do (), la clase B decoró el método do (), pero porque uso la recursividad dentro de run () y lo inicio desde el método run () de la clase A, llama a su propia versión de do (), y no a la decorada. -

Su pregunta podría hacer con algunas aclaraciones, pero si la leí correctamente, querrá "piratear" un código ya escrito en otro módulo, de modo que instancia un objeto modificado en lugar del que normalmente usa. Si es así, esta respuesta le dirá una forma de hacerlo. -

4 Respuestas

El problema era que no podía usar func (self) como método. La razón es que el método setattr () no vincula la función, y la función actúa como un método estático, no un método de clase, gracias a la naturaleza introspectiva de Python, pude encontrar esta solución:

def decorator(obj):
    old_func = obj.func # can't call 'by name' because of recursion

    def decorated_func(self):
        # do my stuff here
        old_func() # does not need pass obj
        # do some othere stuff here

    # here is the magic, this get the type of a 'normal method' of a class
    method = type(obj.func)

    # this bounds the method to the object, so self is passed by default 
    obj.func = method(decorated_func, obj)

Creo que esta es la mejor manera de decorar el método de un objeto en tiempo de ejecución, aunque sería bueno encontrar una manera de llamar method() directamente, sin la línea method = type(obj.func)

contestado el 11 de mayo de 11 a las 04:05

Es posible que desee utilizar __getattribute__ en lugar de __getattr__ (este último solo se llama si falla la búsqueda "estándar"):

class GraphDecorator(object):
    def __init__(self, wrappee):
        self.__wrappee = wrappee

    def method(self):
        # do my stuff here
        self.wrappe.method()
        # do more of stuff here

    def __getattribute__(self, name):
        try:
            wrappee = object.__getattribute__(self, "_GraphDecorator__wrappee")
            return getattr(wrappee, name)
        except AttributeError:
            return object.__getattribute__(self, name)

contestado el 10 de mayo de 11 a las 17:05

Necesito decorar el método de un objeto. Debe estar en tiempo de ejecución porque los decoradores aplicados al objeto dependen de los argumentos que el usuario dio al llamar al programa (argumentos proporcionados con argv), por lo que un mismo objeto podría decorarse 3 veces, 2 veces o no decorarse en todos.

Lamentablemente, lo anterior es incorrecto y lo que está intentando hacer es innecesario. Puede hacer esto en tiempo de ejecución como tal. Ejemplo:

import sys
args = sys.argv[1:]

class MyClass(object):
    pass

if args[0]=='--decorateWithFoo':
    MyClass = decoratorFoo(MyClass)
if args[1]=='--decorateWithBar'
    MyClass = decoratorBar(MyClass)

La sintaxis:

@deco
define something

Es lo mismo que:

define something
something = deco(something)

También podrías hacer una fábrica de decoradores. @makeDecorator(command_line_arguments)

contestado el 11 de mayo de 11 a las 04:05

No entiendo qué es incorrecto, eso es lo que hice. Yo se que la expresion @deco es un azúcar de sintaxis para func = deco(func), pero no hace lo mismo para métodos, si decoras un método mientras estás definiendo la clase, creará un method, prueba esto: type(class().method) después de haber definido la clase y decorado el método. Si decoraste llamando a la función después, crea un function, intentar: type(deco(class().method)). - Hack de Augusto

"Debe estar en tiempo de ejecución porque los decoradores aplicados al objeto dependen de los argumentos que el usuario dio al llamar al programa"

No usan decoradores. Los decoradores son solo soporte sintáctico para envoltorios, también puede usar llamadas normales a funciones / métodos en su lugar.

Respondido 02 Oct 11, 09:10

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.