1

I ran into an issue with an inner function I wrote regarding variable scope. I've managed to simply the code down to point to the exact problem. So starting with:

def outer(foo):
    def inner(bar):
        print(foo)
        print(bar)
    return inner 


if __name__ == "__main__":
    function = outer("foo")
    function("bar")

I'll get the expected output of :

foo
bar 

However, if I try and re-assign foo, I get an error:

def outer(foo):
    def inner(bar):
        foo = "edited " + foo
        print(foo)
        print(bar)
    return inner

Which gives:

UnboundLocalError: local variable 'foo' referenced before assignment  

This reminds me of globals, where you can "read" them normally but to "write" you have to do "global variable; variable=...". Is it the same situation? If so, is there a keyword that allows modification of foo from within inner? Also why? Surely once I've created the function then foo is fixed for that function. I.e. not global. What problem is being avoided here?

1 Answer 1

3

[...] is there a keyword that allows modification of foo from within inner?

There is indeed such a keyword: nonlocal

def outer(foo):
    def inner(bar):
        nonlocal foo
        foo = "edited " + foo
        print(foo)
        print(bar)
    return inner

outer("foo")("bar")

Output:

edited foo
bar

From the docs:

nonlocal_stmt ::= "nonlocal" identifier ("," identifier)*

The nonlocal statement causes the listed identifiers to refer to previously bound variables in the nearest enclosing scope excluding globals. This is important because the default behavior for binding is to search the local namespace first. The statement allows encapsulated code to rebind variables outside of the local scope besides the global (module) scope.

Names listed in a nonlocal statement, unlike those listed in a global statement, must refer to pre-existing bindings in an enclosing scope (the scope in which a new binding should be created cannot be determined unambiguously).

Names listed in a nonlocal statement must not collide with pre-existing bindings in the local scope.

As for the why part:

Any assignment (x = y) within a code block changes the scope of the variable to the current scope. Thus to reference and modify (*) a variable from the global scope we need the keyword global, but in this case we need the variable from the "nearest enclosing" scope, thus we need the keyword nonlocal.

(*) modify via assignment; mutable global variables can still be modified with their respective methods w/o global/nonlocal keyword.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.