开发者

How to decorate an object method?

开发者 https://www.devze.com 2023-03-04 17:12 出处:网络
I need to decorate a object\'s method. It needs to be at runtime because the decorators applied on the object depends on the arguments that the user gave when calling the program (arguments supplied w

I need to decorate a object's method. It needs to be at runtime because the decorators applied on the object depends on the arguments that the user gave when calling the program (arguments supplied with argv), so a same object could be decorated 3 times, 2 times, or not be decorated at all.

Here is some context, the program is a puzzle solver, the main behavior is to find a solution for the puzzle automatically, by automatically I mean without user intervention. And here is where the decoration gets to play, one of the things I want to is draw a graph of what happened 开发者_StackOverflowduring the execution, but I want to do so only when the flag --draw-graph is used.

Here is what I've tried:

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)

And why it did NOT work: It did not work because of the way I built the application, when a method that did not exist in my Decorator class was called it felt back to the implementation of the decorated class, the problem is that the application always started invoking the method run that did not need to be decorated, so the undecorated fall back was used and from inside the undecorated form it always called undecorated methods, what I needed was to replace the method from the object, not to proxy it:

# 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

And here is my problem, the decorated form does not receive self when it is called resulting on a TypeError because of the wrong number of arguments.


The problem was that I couldn't use func(self) as a method. The reason is that setattr() method does not bound the function, and the function acts like it a static method - not a class method -, thanks to the introspective nature of python I've able to come up with this solution:

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)

I think this is the best way to decorate a object's method at runtime, though it would be nice to find a way to call method() directly, without the line method = type(obj.func)


You might want to use __getattribute__ instead of __getattr__ (the latter being only called if "standard" lookup fails):

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)


I need to decorate a object's method. It needs to be at runtime because the decorators applied on the object depends on the arguments that the user gave when calling the program (arguments supplied with argv), so a same object could be decorated 3 times, 2 times, or not be decorated at all.

The above is unfortunately incorrect, and what you are trying to do is unnecessary. You can do this at runtime like so. Example:

import sys
args = sys.argv[1:]

class MyClass(object):
    pass

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

The syntax:

@deco
define something

Is the same thing as:

define something
something = deco(something)

You could also make a decorator factory @makeDecorator(command_line_arguments)


"It needs to be at runtime because the decorators applied on the object depends on the arguments that the user gave when calling the program"

The don't use decorators. Decorators are only syntactical support for wrappers, you can just as well use normal function/method calls instead.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号