【问题标题】:Can a decorated function access variables of the decorator装饰函数可以访问装饰器的变量吗
【发布时间】:2012-09-15 08:21:23
【问题描述】:

我试图了解装饰器是如何工作的,并且想知道装饰函数是否可以访问装饰器的变量。例如,在下面的代码中,如何让 f1 可以访问 localVariable?这可能吗?这甚至是一种很好的做事方式吗?

def funcDec(func):
    localVariable = "I'm a local string"
    def func2Return(*args):                                                                            
        print "Calling localVariable from decorator " + localVariable
        func(*args)                                      
        print "done with calling f1"
    return func2Return

@funcDec
def f1(x, y):
    print x + y
    print localVariable

f1(2, 3)

【问题讨论】:

    标签: python


    【解决方案1】:

    由于 Python 的作用域规则,装饰函数通常无法访问装饰器中的任何变量。但是,由于函数可以分配给它们的任意属性,您可以在装饰器中执行以下操作以获得类似的效果(由于相同的范围规则):

    def funcDec(func):
        localVariable = "I'm a local string"
    
        def wrapped(*args):
            print("Calling localVariable from funcDec " + localVariable)
            func(*args)
            print("done with calling f1")
    
        wrapped.attrib = localVariable
        return wrapped
    
    @funcDec
    def f1(x, y):
        print(x + y)
        print('f1.attrib: {!r}'.format(f1.attrib))
    
    f1(2, 3)
    

    这将产生以下输出:

    Calling localVariable from funcDec I'm a local string
    5
    f1.attrib: "I'm a local string"
    done with calling f1
    

    有人问这是否可以应用于类的方法: 答案是“是”,但您必须通过类本身或作为self 参数传递的它的实例来引用该方法。这两种技术如下所示。最好使用self,因为它使代码独立于它所在的类的名称。

    class Test(object):
        @funcDec
        def f1(self):
            print('{}.f1() called'.format(self.__class__.__name__))
            print('self.f1.attrib: {!r}'.format(self.f1.attrib))  # Preferred.
            print('Test.f1.attrib: {!r}'.format(Test.f1.attrib))  # Also works.
    
    print()
    test = Test()
    test.f1()
    

    输出:

    Calling localVariable from funcDec I'm a local string
    Test.f1() called
    self.f1.attrib: "I'm a local string"
    Test.f1.attrib: "I'm a local string"
    done with calling f1
    

    【讨论】:

    • 很好的例子!类中的方法也可以这样做吗?
    • @KeerthanaPrabhakaran:是的,装饰器可以在类方法上工作,但他们需要以不同的方式引用添加的属性:即类似self.method_name.attrib
    • 是的,我确实尝试过!但是有一个例外说'instancemethod' object has no attribute 'attrib'
    • @KeerthanaPrabhakaran:没有看到您的代码,我猜是因为您在装饰方法中引用属性的方式与装饰函数中显示的方式相同。对于方法,必须将其更改为使用作为self 参数传递的实例。请参阅我的答案的最新更新,其中显示了一个工作示例,说明了需要更改的内容以及如何更改。
    【解决方案2】:

    不,你不能。见this previous question。仅仅因为该函数是一个装饰器并不意味着它调用的函数对其变量具有特殊访问权限。如果你这样做:

    def func():
        a = 2
        otherFunc()
    

    那么 otherFunc 无权访问变量a。这就是它对所有函数调用的工作方式,也是它对装饰器的工作方式。

    现在,您在装饰器中定义的包装函数(在您的示例中为func2Return确实 可以访问变量,因为该函数在词法上与这些变量在同一范围内。所以你的print "Calling localVariable from decorator " + localVariable 行会起作用。您可以在某种程度上使用它来包装装饰函数,其行为取决于装饰器中的变量。但实际被修饰的函数(在您的示例中为f1)无权访问这些变量。

    函数只能从函数定义实际所在的范围内访问局部变量。函数不会从调用范围中获取变量。 (这是一件好事。如果他们这样做了,那将是一团糟。)

    【讨论】:

    【解决方案3】:

    我认为如果您记住装饰器会有所帮助

    @deco
    def f(...): ...
    

    只是

    的语法糖
    def f(...): ...
    f = deco(f)
    

    而不是某种宏扩展。在 Python 中,变量的范围是静态确定的,因此对于全局(模块级)函数,既不作为参数传递也不分配给的变量将在全局命名空间中查找。

    因此,您必须显式传递 func2Return() 的局部变量。将f1 的签名更改为f1(x, y, localvariable=None) 并让包装函数fun2Return 使用

    调用它
    f1(*args, localvariable=localvariable)
    

    【讨论】:

      猜你喜欢
      • 2020-05-20
      • 2020-05-02
      • 1970-01-01
      • 2011-06-25
      • 1970-01-01
      • 2022-06-11
      • 2016-11-24
      • 2016-02-08
      • 1970-01-01
      相关资源
      最近更新 更多