pythonpython-nonlocal

Is it possible to modify a variable in python that is in an outer (enclosing), but not global, scope?


Consider this example:

def A():
    b = 1
    def B():
        # I can access 'b' from here.
        print(b)
        # But can i modify 'b' here?
    B()
A()

For the code in the B function, the variable b is in a non-global, enclosing (outer) scope. How can I modify b from within B? I get an UnboundLocalError if I try it directly, and using global does not fix the problem since b is not global.


Python implements lexical, not dynamic scope - like almost all modern languages. The techniques here will not allow access to the caller's variables - unless the caller also happens to be an enclosing function - because the caller is not in scope. For more on this problem, see How can I access variables from the caller, even if it isn't an enclosing scope (i.e., implement dynamic scoping)?.


Solution

  • On Python 3, use the nonlocal keyword:

    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.

    def foo():
        a = 1
        def bar():
            nonlocal a
            a = 2
        bar()
        print(a)  # Output: 2
    

    On Python 2, use a mutable object (like a list, or dict) and mutate the value instead of reassigning a variable:

    def foo():
        a = []
        def bar():
            a.append(1)
        bar()
        bar()
        print a
    
    foo()
    

    Outputs:

    [1, 1]