Rebinding variable outside of the local scope

The other day I had to explain why in Python in contrast to Perl/Lisp/Scheme one can’t rebind variable outside of the local scope besides the global scope, i.e. the following function will result in an UnboundLocalError exception in Python 2.x

def outer():
    count = 0
    def inner():
        count += 1 # rebinding
    inner()
    print count
>>> outer()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "scope.py", line 8, in outer
    inner()
  File "scope.py", line 6, in inner
    count += 1 # rebinding
UnboundLocalError: local variable 'count' referenced before assignment

There are several workarounds.

For one thing it is possible to use a variable holding a mutable object:

def outer():
    ns = dict(count=0)
    def inner():
        ns['count'] += 1 # rebinding
    inner()
    print ns['count']
>>> outer()
1

Another approach is to use a function object itself to store the variable:

def outer():
    outer.count = 0
    def inner():
        outer.count += 1 # rebinding
    inner()
    print outer.count
>>> outer()
1

The good news is that there is no need for workarounds anymore in Python 3 which introduced nonlocal keyword:

def outer():
    count = 0
    def inner():
        nonlocal count
        count += 1 # rebinding
    inner()
    print(count)
>>> outer()
1

2 Responses to “Rebinding variable outside of the local scope”

  1. xtian says:

    Nice post! Your second workaround is a bit dodgy, though – rather than modifying non-local state you’re really modifying global state. If you were returning the inner function, rather than calling it, the closed-over state would be shared between different calls to outer.

    I often use a 1-element list as the state box for a mutating closure.

    *sigh* I love closures.

    • Ruslan Spivak says:

      Hi xtian,

      Probably my example didn’t emphasize it, but ‘outer’ function could be nested into another function (thus no global state modification) and all I was caring about was a variable in the outside scope and how to modify it.

      Maybe the following example explains my intentions better than the old one:

      # main function
      def outer():
          outer.error = False
      
          # inner function does all the job and modifies the flag
          def inner(index):
              if index == 5:
                  outer.error = True
      
          for index in range(10):
              inner(index)
      
          return 'FAILURE' if outer.error else 'SUCCESS'
      

      >>> outer()

      Quite often I would use classes and avoid workarounds altogether by storing state in a class instance.

Leave a Reply