UnboundLocalError with operators and functional programming in python (methods work fine)

Question:

prog1.py:

def runf(f):
    f()

def main():
    l = [0]
    def f():
        l.append(1)
    runf(f)
    print(l)

main()

Gives me (as expected):

[0, 1]

prog2.py:

def runf(f):
    f()

def main():
    l = [0]
    def f():
        l += [1] # <-- Only difference
    runf(f)
    print(l)

main()

Gives me:

Traceback (most recent call last):
  File "prog2.py", line 11, in <module>
    main()
  File "prog2.py", line 8, in main
    runf(f)
  File "prog2.py", line 2, in runf
    f()
  File "prog2.py", line 7, in f
    l += [1]
UnboundLocalError: local variable 'l' referenced before assignment

Could someone please explain to me what’s going on here?

Note: This happens in both python2 and python3.

Also, I’m open to suggestions on a better title for this question.

Asked By: Conley Owens

||

Answers:

The execution model reference of Python (section 4.1) states:

If a name is bound in a block, it is a
local variable of that block.

What happens is that l += [1], for the sake of binding is equivalent to l = l + [1] which means l is bound inside f. Here’s another interesting document reference:

Assignment of an object to a single target is recursively defined as follows.

If the target is an identifier (name):

  • If the name does not occur in a global statement in the current code
    block: the name is bound to the object
    in the current local namespace.
  • Otherwise: the name is bound to the object in the current global
    namespace.

The otherwise clause is relevant here. Since you did not declare global l in the scope of f and assigned to l, the name is bound in the local namespace of f. Then, the reference to it implicitly created by l += [1] refers to a variable that hasn’t been defined yet. Hence the UnboundLocalError.


P.S. global l wouldn’t help you, by the way. Python 3 has the nonlocal statement to handle cases like this:

The nonlocal statement causes the
listed identifiers to refer to
previously bound variables in the
nearest enclosing scope. 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.

Answered By: Eli Bendersky