Python 修饰函数可以访问装饰器的变量吗

Python 修饰函数可以访问装饰器的变量吗,python,Python,我试图理解decorator是如何工作的,我想知道一个修饰过的函数是否可以访问decorator的变量。例如,在下面的代码中,如何使f1能够访问localVariable?这可能吗?这是一种很好的做事方式吗 def funcDec(func): localVariable = "I'm a local string" def func2Return(*args):

我试图理解decorator是如何工作的,我想知道一个修饰过的函数是否可以访问decorator的变量。例如,在下面的代码中,如何使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)
不,你不能。看见仅仅因为函数是一个装饰器并不意味着它调用的函数对它的变量有特殊的访问权限。如果您这样做:

def func():
    a = 2
    otherFunc()
则otherFunc无权访问变量
a
。这就是它对所有函数调用的工作方式,也是它对装饰程序的工作方式

现在,您在decorator中定义的包装函数(示例中为
func2Return
)确实可以访问变量,因为该函数在词汇上与这些变量在同一范围内。因此,您的行
print“从装饰程序调用localVariable”+localVariable
将起作用。您可以在某种程度上使用它来包装装饰函数,使其行为取决于装饰器中的变量。但是实际被修饰的函数(
f1
,在您的示例中)没有访问这些变量的权限


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

我认为如果你记住一个装饰师

@deco
def f(...): ...
只是语法上的糖

def f(...): ...
f = deco(f)
而不是某种宏观扩张。在Python中,变量的作用域是静态确定的,因此对于全局(模块级)函数,既没有作为参数传递也没有指定给的变量将在全局命名空间中查找

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

f1(*args, localvariable=localvariable)

由于Python的作用域规则,修饰函数通常无法访问修饰器中的任何变量。但是,由于函数可以具有指定给它们的任意属性,因此可以在decorator中执行以下操作以获得类似的效果(由于相同的作用域规则):

这将产生以下输出:

从funcDec调用localVariable我是一个本地字符串
5.
f1.attrib:“我是本地字符串”
完成调用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()
输出:

从funcDec调用localVariable我是一个本地字符串
调用Test.f1()
self.f1.attrib:“我是本地字符串”
Test.f1.attrib:“我是本地字符串”
完成调用f1

另请参见:好例子!类中的方法也可以这样做吗?@KeerthanaPrabhakaran:是的,修饰符可以处理类方法,但它们需要以不同的方式引用添加的属性:例如,类似于
self.method\u name.attrib
。是的,我尝试过了!但是出现了一个异常,说
'instancemethod'对象没有属性“attrib”
@KeerthanaPrabhakaran:如果没有看到您的代码,我猜是因为您在修饰方法中引用属性的方式与修饰函数中所示的方式相同。对于方法,必须将其更改为使用作为
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()