Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python decorator for a class method to change the current object instance i.e, self

The following is my test code to have a decorator for a class method which can change the self (current object instance)

class Test(object):
    def __init__(self):
        self.x = 5

    def _change_x(self):
        print "in _change_x"
        def decorator(f):
            print "in decorator"
            def wrapped(*args, **kwargs):
                print "in wrapped"
                print args, kwargs
                self.x = 8
                f(*args, **kwargs)
            return wrapped
        return decorator

    @_change_x
    def print_x(self):
        print "in print_x, x: %d" % self.x
        return self.x

if __name__ == "__main__":
    test = Test()
    print "Initial value for x: %d" % test.x
    print "Test if it changed? x: %d" % test.print_x()

The internal method wrapped in the decorator is not being invoked. Can any one help me pointing out the mistake in my test code. I want this kind of decorator defined in a class, and is used to decorate few methods in that class to change the class members at the call time.

like image 612
RPM Avatar asked Oct 17 '25 03:10

RPM


1 Answers

First, lets consider why your code doesn't work...

At the time when _change_x is invoked, the class is still under construction, so there can't be any instances of the class. Because of this, your self argument is misleading. When it gets called, self is actually the function print_x. Obviously adding things to self won't change your instance of Test because self isn't an instance of Test :-). In other words, there is nothing special about defining the decorator function inside the class. The only thing that it does is it also adds a function _change_x to the namespace that you could call at other times.

How do we get this working then? Well, the answer is to realize that _change_x is only decorating methods. In that case, the first argument to the wrapper function will be self (just like in a normal method), so we can drop it in there and work with it inside of wrapped: This works:

class Test(object):

    def __init__(self):
        self.x = 5

    def _change_x(f):
        print "in _change_x"
        def wrapped(self, *args, **kwargs):
            print "in wrapped"
            print args, kwargs
            self.x = 8
            return f(self, *args, **kwargs)
        return wrapped

    @_change_x
    def print_x(self):
        print "in print_x, x: %d" % self.x
        return self.x

if __name__ == "__main__":
    test = Test()
    print "Initial value for x: %d" % test.x
    print "Test if it changed? x: %d" % test.print_x()

But it turns out that you don't need to define the decorator in the class at all. After all, as I said earlier -- There is nothing special about putting it in the class...:

def _change_x(f):
    print "in _change_x"
    def wrapped(self, *args, **kwargs):
        print "in wrapped"
        print args, kwargs
        self.x = 8
        return f(self, *args, **kwargs)
    return wrapped


class Test(object):

    def __init__(self):
        self.x = 5

    @_change_x
    def print_x(self):
        print "in print_x, x: %d" % self.x
        return self.x

if __name__ == "__main__":
    test = Test()
    print "Initial value for x: %d" % test.x
    print "Test if it changed? x: %d" % test.print_x()
like image 95
mgilson Avatar answered Oct 18 '25 17:10

mgilson