Local functions in Python

46,914

Solution 1

I believe you're correct in seeing this as a "mutability" problem. While the code you posted does throw an "UnboundLocalError", the following code does not:

def outer():
    a = 0
    def inner():
        print a
    inner()
outer()

Python doesn't allow you to reassign the value of a variable from an outer scope in an inner scope (unless you're using the keyword "global", which doesn't apply in this case).

Check out the bottom section of the "classes" documentation in this Python 2.6.2 documentation:

9.2. Python Scopes and Namespaces

[…] If a name is declared global, then all references and assignments go directly to the middle scope containing the module’s global names. Otherwise, all variables found outside of the innermost scope are read-only (an attempt to write to such a variable will simply create a new local variable in the innermost scope, leaving the identically named outer variable unchanged).

Your "UnboundLocalError" is because your function is actually declaring a new variable called "a" and then immediately trying to do a "+=" operation on it, but this fails because "a" does not have a value yet. (View the "a+=1" as "a = a+1" and you can see the problem if "a" is undefined).

In general, if you're going to want to modify "a", the way people usually get around it is to use a mutable type to pass "a" around (such as a list or a dictionary). You can modify "a" via the contents of the mutable type (as you probably noticed in your testing with this setup).

Hope that helps!

Solution 2

You should specify your variable as nonlocal to preserve it's state in closure, so definition should be like this

def outer():
    a = 0
    def inner():
        nonlocal a
        a += 1
    inner()

Solution 3

Try binding the variable as an argument.

def outer():
    a = 0
    def inner(a=a):
        a += 1

    inner()

outer()

I'll try and dig up the appropriate documents.

edit

Since you want the inner function to have a side effect on the outer scope, then you need to use a mutable datatype like a list. Integers and strings are immutable.

def outer():
    a = [0]
    def inner():
        a[0] += 1
    inner()
    print a[0]
outer()
Share:
46,914

Related videos on Youtube

Matt Joiner
Author by

Matt Joiner

About Me I like parsimonious code, with simple interfaces and excellent documentation. I'm not interested in enterprise, boiler-plate, or cookie-cutter nonsense. I oppose cruft and obfuscation. My favourite languages are Go, Python and C. I wish I was better at Haskell. Google+ GitHub Bitbucket Google code My favourite posts http://stackoverflow.com/questions/3609469/what-are-the-thread-limitations-when-working-on-linux-compared-to-processes-for/3705919#3705919 http://stackoverflow.com/questions/4352425/what-should-i-learn-first-before-heading-to-c/4352469#4352469 http://stackoverflow.com/questions/6167809/how-much-bad-can-be-done-using-register-variables-in-c/6168852#6168852 http://stackoverflow.com/questions/4141307/c-and-c-source-code-profiling-tools/4141345#4141345 http://stackoverflow.com/questions/3463207/how-big-can-a-malloc-be-in-c/3486163#3486163 http://stackoverflow.com/questions/4095637/memory-use-of-stl-data-structures-windows-vs-linux/4183178#4183178

Updated on January 26, 2021

Comments

  • Matt Joiner
    Matt Joiner over 3 years

    In the following Python code, I get an UnboundLocalError. As I understand it, local functions share the local variables of the containing function, but this hardly seems to be the case here. I recognise that a is an immutable value in this context, but that should not be a problem.

    def outer():
        a = 0
        def inner():
            a += 1
        inner()
    outer()
    

    It would seem that the inner function has received copies of all the references in the parent function, as I do not get the UnboundLocalError exception if the value of a is wrapped in a mutable type.

    Is someone able to clarify the behaviour here, and point me to the appropriate Python documentation on this?

  • Matt Joiner
    Matt Joiner over 14 years
    I have found this suggestion under documentation from older versions of Python. There is still the issue that incrementing a will not affect the outer() a.
  • Matt Joiner
    Matt Joiner over 14 years
    Yes I believe inner is receiving a copy of all the outer references. However on assignment to the immutable one's, the new value is only bound to the inner functions scope.
  • Unknown
    Unknown over 14 years
    If you want it to have a side effect, then you need to use a mutable type for a.
  • Matt Joiner
    Matt Joiner over 14 years
    I guess the only question I have now then is if locals() != the "innermost scope" as described in the documentation you linked to.
  • Ned Deily
    Ned Deily over 14 years
    BTW, Python3 addresses this by adding the nonlocal statement: docs.python.org/3.1/reference/…
  • Anthony Hall
    Anthony Hall over 10 years
    Beware, however. I tried this in Python 2.7.6 and got a syntax error. According to this answer, stackoverflow.com/a/14264325/1286986, 'nonlocal' only works in Python 3.