开发者

Losing variable scope inside a Python decorator

开发者 https://www.devze.com 2023-02-12 19:47 出处:网络
This simple decorator works as expected: def protect(*permissions): def outer(f): def inner(*args): print permissions[0]

This simple decorator works as expected:

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

The output is:

protected
something

Moving beyond a Python shell and using the decorator in the larger scope of my project, something strange is happening: while inside of the inner function, permissions is not defined.

I'm thinking there must be some Python 开发者_运维技巧variable scoping/decorator subtleties I'm unaware of that could be causing this. Any insights are appreciated.


In my mind I could figure out what is going on - let me try to spell it out:

it has to do with Python not "perceiving" the "permissions" variable as existing on the scopes outside the "inner" function - since when "inner" itself is defined, "permissions" would long have been defined in the 'outsidemost' scope of protect. Thus, when compiling inner the variale is taken as being a global variable. (That is why the exact error message is needed - NameErrors can be for local variables used before definition, or for non-existing global variables - the exact message will tell much in this case)

In other words, you most likley have hit an implementation bug - please try to expose the minimum ammount of code that causes the issue and the exact python version you are using. If possible try with the latest micro version available for you - and ten it will be time to open an issue at bugs.python.org

I see two workarounds -- the first one would be a hack that would confirm my diagnosis - and might not work at all: Make a reading access to the permissions variable on the outer function, outside inner's body: that should make the interpretor "perceive" it as a non-local variable in outer, and propagate it into inner.

The other workaround is more solid and consistent - and maybe even better code-styling for yu in this case: to use a class as the decorator in this case, instead of relying on multiple nested functions and its closures.

The above snippet could be rewritten as:

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

This code does not rely on nested closures, thus avoidnign the bug you've hit. Besides, it follows the "flat is better than nested" and "explict is better than implicit" coding guidelines.

But please, help everyone to trace this bug, by giving us your version and code that actually triggers this behavior, if not opening an issue at python.org


I ran into this problem once, it was because you cannot set a variable that you put in a closure. Don't know if it's the case for you, but it's good to know.

0

精彩评论

暂无评论...
验证码 换一张
取 消