【问题标题】:Python decorator for a class method to change the current object instance i.e, self用于更改当前对象实例的类方法的 Python 装饰器,即 self
【发布时间】:2016-07-13 21:46:03
【问题描述】:

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

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

包装在装饰器中的内部方法未被调用。谁能帮我指出我的测试代码中的错误。 我希望在一个类中定义这种装饰器,用于装饰该类中的几个方法,以便在调用时更改类成员。

【问题讨论】:

  • 除了装饰器之外,任何人都可以建议我在每次函数调用之前更改类成员(对于该实例)。为了便于阅读,我希望修改不透明,这就是为什么我不在类方法的开头修改它们。
  • 我有点难以理解为什么你想在函数调用之前改变事情......我不知道手头的问题,但这似乎是一个不直观的设计模式任何我能想到的用例(诚然,目前还没有用例 ;-))。
  • @mgilson 在许多方法调用之前插入一个额外的步骤是一个非常强大的调试工具。简单的print f 将指示在整个程序过程中的不同点调用了哪些方法。
  • @TadhgMcDonald-Jensen -- 当然,我可以买那个(有点......pdb 恕我直言仍然是一个更好的选择)。但即使在这种情况下,您也没有改变任何东西,只是检查。
  • 如果要统计方法被调用的次数怎么办?保留一个 count 属性并在每次调用之前增加它,是的,我知道也有一些有用的工具,但是创建自己的装饰器是控制所采取的操作的最简单方法。

标签: python python-decorators


【解决方案1】:

首先,让我们考虑一下为什么您的代码不起作用...

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

那我们如何让它工作呢?嗯,答案是要意识到_change_x 只是装饰方法。在这种情况下,包装函数的第一个参数将是 self(就像在普通方法中一样),因此我们可以将其放入其中并在 wrapped 中使用它:这可行:

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

【讨论】:

  • 我想要在类中定义这种装饰器”会暗示 OP 特别希望在类定义中使用装饰器
  • @TadhgMcDonald-Jensen -- 是的,我还不太了解这里的用例。如果 OP 更新问题/cmets,也许会变得更清楚?
【解决方案2】:

问题在于 _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),你仍然可以在类定义之后作为静态方法使用该函数,尽管在你完成之前你不能这样做与它在同一范围内。 (除非你做了@_change_x.__get__(0) 这真的没必要)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-02-14
    • 2019-02-03
    • 2013-02-12
    相关资源
    最近更新 更多