在Python装饰器中丢失变量范围

时间:2011-02-23 20:35:23

标签: python scope decorator

这个简单的装饰工作符合预期:

def protect(*permissions):
    def outer(f):
        def inner(*args):
            print permissions[0]
            return f(*args)  
        return inner  
    return outer

@protect('protected')
def func(var):
    return var

print func('something')

输出结果为:

protected
something

超越Python shell并在我项目的更大范围内使用装饰器,发生了一些奇怪的事情:在inner函数内部,permissions未定义。

我认为必须有一些Python变量作用域/装饰器细微之处,我不知道这可能导致这种情况。任何见解都表示赞赏。

2 个答案:

答案 0 :(得分:1)

在我的脑海里,我可以弄明白发生了什么 - 让我试着拼出来:

它与Python有关,而不是“感知”“内部”函数之外的范围内存在的“权限”变量 - 因为当“内部”本身被定义时,“权限”将长期定义在'最外面的保护范围。因此,在编译inner时,变量被视为全局变量。 (这就是为什么需要完全错误消息 - NameErrors可以用于定义之前使用的局部变量,或者用于不存在的全局变量 - 在这种情况下确切的消息会说明很多)

换句话说,你最有可能遇到一个实现错误 - 请尝试公开导致问题的最小代码和你正在使用的python版本。如果可能,请尝试使用最新的微型版本 - 十次是时候在bugs.python.org上打开一个问题

我看到两个解决方法 - 第一个是确认我的诊断的黑客 - 并且可能根本不起作用:对permissions函数上的outer变量进行读取访问,在inner之外的身体:这应该使解释者将其“感知”为外部的非局部变量,并将其传播到内部。

另一种解决方法是更加可靠和一致 - 在这种情况下甚至可能更好的代码样式:在这种情况下使用类作为装饰器,而不是依赖于多个嵌套函数及其闭包。

以上代码段可以改写为:

class Protect(object):
    def __init__(self, *permissions):
        self.permissions = permissions
    def __call__(self, f):
        def inner(*args):
            print self.permissions[0]
            return f(*args)  
        return inner  

@Protect('protected')
def func(var):
    return var

print func('something')

此代码不依赖于嵌套闭包,因此避免指出您遇到的错误。此外,它遵循“扁平比嵌套更好”和“明确优于隐式”编码指南。

但请通过向我们提供实际触发此行为的版本和代码,帮助每个人追踪此错误,如果不是在python.org上打开问题

答案 1 :(得分:0)

我遇到过这个问题一次,原因是你无法设置一个放在闭包中的变量。不知道你是否属于这种情况,但很高兴知道。