Python closure function losing outer variable access

I just learned python @ decorator, it's cool, but soon I found my modified code coming out weird problems.

def with_wrapper(param1):
    def dummy_wrapper(fn):
        print param1
        param1 = 'new'
        fn(param1)
    return dummy_wrapper

def dummy():
    @with_wrapper('param1')
    def implementation(param2):
        print param2

dummy()

I debug it, it throws out exception at print param1

UnboundLocalError: local variable 'param1' referenced before assignment

If I remove param1 = 'new' this line, without any modify operation (link to new object) on variables from outer scope, this routine might working.

Is it meaning I only have made one copy of outer scope variables, then make modification?


Thanks Delnan, it's essential to closure. Likely answer from here: What limitations have closures in Python compared to language X closures?

Similar code as:

def e(a):
    def f():
        print a
        a = '1'
    f()
e('2')

And also this seems previous annoying global variable:

a = '1'
def b():
    #global a
    print a
    a = '2'
b()

This is fixed by add global symbol. But for closure, no such symbol found. Thanks unutbu, Python 3 gave us nonlocal.

I know from above directly accessing to outer variable is read-only. but it's kind of uncomfortable to see preceded reading variable(print var) is also affected.


When Python parses a function, it notes whenever it finds a variable used on the left-hand side of an assignment, such as

param1 = 'new'

It assumes that all such variables are local to the function. So when you precede this assignment with

print param1

an error occurs because Python does not have a value for this local variable at the time the print statement is executed.


In Python3 you can fix this by declaring that param1 is nonlocal:

def with_wrapper(param1):
    def dummy_wrapper(fn):
        nonlocal param1
        print param1
        param1 = 'new'
        fn(param1)
    return dummy_wrapper

In Python2 you have to resort to a trick, such as passing param1 inside a list (or some other mutable object):

def with_wrapper(param1_list):
    def dummy_wrapper(fn):
        print param1_list[0]
        param1_list[0] = 'new'   # mutate the value inside the list
        fn(param1_list[0])
    return dummy_wrapper

def dummy():
    @with_wrapper(['param1'])   # <--- Note we pass a list here
    def implementation(param2):
        print param2

You assign param1 in the function, which makes param1 a local variable. However, it hasn't been assigned at the point you're printing it, so you get an error. Python doesn't fall back to looking for variables in outer scopes.


Handy for temporary situations or exploratory code (but otherwise not good practice) :

If you're wanting to capture a variable from the outer scope in Python 2.x then using global is also an option (with the usual provisos).

While the following will throw (assignment of outer1 within inner makes it local and hence unbounded in the if condition):

def outer():
    outer1 = 1
    def inner():
        if outer1 == 1:
            outer1 = 2
            print('attempted to accessed outer %d' % outer1)

This will not:

def outer():
    global outer1
    outer1 = 1
    def inner():
        global outer1
        if outer1 == 1:
            outer1 = 2
            print('accessed outer %d' % outer1)
链接地址: http://www.djcxy.com/p/51252.html

上一篇: 需要帮助了解Python关闭

下一篇: Python关闭函数失去外部变量访问