Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
427 views
in Technique[技术] by (71.8m points)

python - Why doesn't this closure modify the variable in the enclosing scope?

This bit of Python does not work:

def make_incrementer(start):
    def closure():
        # I know I could write 'x = start' and use x - that's not my point though (:
        while True:
            yield start
            start += 1
    return closure

x = make_incrementer(100)
iter = x()
print iter.next()    # Exception: UnboundLocalError: local variable 'start' referenced before assignment

I know how to fix that error, but bear with me:

This code works fine:

def test(start):
    def closure():
        return start
    return closure

x = test(999)
print x()    # prints 999

Why can I read the start variable inside a closure but not write to it? What language rule is causing this handling of the start variable?

Update: I found this SO post relevant (the answer more than the question): Read/Write Python Closures

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Whenever you assign a variable inside of a function it will be a local variable for that function. The line start += 1 is assigning a new value to start, so start is a local variable. Since a local variable start exists the function will not attempt to look in the global scope for start when you first try to access it, hence the error you are seeing.

In 3.x your code example will work if you use the nonlocal keyword:

def make_incrementer(start):
    def closure():
        nonlocal start
        while True:
            yield start
            start += 1
    return closure

On 2.x you can often get around similar issues by using the global keyword, but that does not work here because start is not a global variable.

In this scenario you can either do something like what you suggested (x = start), or use a mutable variable where you modify and yield an internal value.

def make_incrementer(start):
    start = [start]
    def closure():
        while True:
            yield start[0]
            start[0] += 1
    return closure

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...