Python装饰器用于改变当前对象实例的类方法,即self

时间:2016-03-26 04:53:05

标签: python python-decorators

以下是我的测试代码,它有一个可以改变self(当前对象实例)的类方法的装饰器

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()

没有调用包装在装饰器中的内部方法。任何人都可以帮我指出我的测试代码中的错误。 我希望在类中定义这种装饰器,并用于装饰该类中的几个方法以在调用时更改类成员。

2 个答案:

答案 0 :(得分:3)

首先,让我们考虑为什么你的代码没有工作......

在调用_change_x时,该类仍在构建中,因此不能成为该类的任何实例。因此,您的self参数具有误导性。调用它时,self实际上是函数print_x。显然,向self添加内容不会更改Test的实例,因为self不是Test的实例:-)。 换句话说,在类中定义装饰器函数没有什么特别之处。它唯一能做的就是它还可以在命名空间中添加一个函数_change_x。次。

我们如何让这个工作呢?嗯,答案是要意识到_change_x只是装饰方法。在这种情况下,包装器函数的第一个参数将是self(就像在普通方法中一样),所以我们可以将它放在那里并在wrapped内使用它:这有效:< / p>

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()

但事实证明,你根本不需要在课堂上定义装饰器。毕竟,正如我之前所说的那样 - 把它放在课堂上没什么特别的......:

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()

答案 1 :(得分:0)

问题是_change_x是装饰器本身,包装器需要将self作为参数:

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

这可能会令人困惑,因为作为装饰器,它不是实例上的方法,因此您可能要么在使用它之后将其从类范围中删除,要么在类外定义它范围(仍然以同样的方式)

编辑:如果您在类中定义了_change_x,那么在类定义的底部添加_change_x = staticmethod(_change_x),您仍然可以在类定义之后将该函数作为staticmethod使用,尽管您不能这样做直到你在相同的范围内完成它。 (除非你做了@_change_x.__get__(0)这是非常不必要的)