pythonclosures

Why closure works like this in Python?


I don't quite understand how closure works in Python. It behaves in an unexpected way that I can't comprehend.

def A():
    b = 1
    def B():
        b += 1
        return b
    return B()
print(A())

This results in an UnboundLocalError: local variable 'b' referenced before assignment.

But b is in fact in the closure of B, as followed example works just fine.

def A():
    b = 1
    def B():
        return b + 1
    return B()
print(A())

So in my mind the closure of B has environment {b -> 1} created at the time of declaration of B. Why can't I update it locally?


Solution

  • The information in Patrick Roberts comment pretty much covers it.

    But to be more clear, an inner function can read a variable from an enclosing scope, but can't write to it unless you've declared the variable to already exist (nonlocal or global keywords). Otherwise writing to a variable implicitly declares it local, and in the case of b += 1 tries to read an uninitialized local variable to add 1 to it, which fails.

    As for why, when a local value is captured it becomes a constant, which allows inner functions to be constructed with different values. For example:

    def f(x):
      def g():
        print(x)
      return g
    
    print_yes = f('yes')
    print_no = f('no')
    

    Calling print_yes() will always print "yes", Calling print_no() will print "no", even though parameter x no longer exists.