Discussion forum for David Beazley

Everything I thought I understood about python is wrong


#1

Exhibit A

input

foo = 0


def bar():
    print(foo)


bar()
print(foo)

output

0
0

Exhibit B

input

foo = 0


def bar():
    foo += 1


bar()
print(foo)

output

UnboundLocalError: local variable 'foo' referenced before assignment

Exhibit C

input

foo = []


def bar():
    foo.append('abcd')


bar()
print(foo)

output

['abcd']

Exhibit D

input

foo = []


def bar():
    foo += ['abcd']


bar()
print(foo)

output

UnboundLocalError: local variable 'foo' referenced before assignment

Exhibit C

input

foo = set()


def bar():
    foo.add('abcd')


bar()
print(foo)

output

{'abcd'}

Exhibit E

input

foo = {}


def bar():
    foo['abcd'] = 1234


bar()
print(foo)

output

{'abcd': 1234}

I don’t know if you feel disturbed by this, but I certainly do. Can any superhuman please explain how this exactly works?


#2

This seems to have solved all my misconceptions


#3

The difference between rebinding a variable name and changing an object is often something that confuses newcomers in courses. For example, the difference between this:

def bar(items):
    items.append(42)      # Mutates the object items refers to

def foo(items):
    items = [4, 5, 6]     # Rebinds "items" to a new object

data = [1,2,3]
bar(data)            # 42 gets appended onto data
foo(data)            # Nothing happens

The += operator case is a bit more interesting. For that, it’s important to note that Python analyzes the bodies of all functions in advance. Since a += x is treated as a = a + x it appears as if you are assigning a value to a local variable a. The cryptic error message about reading a variable before assignment is caused by a not have a proper value before carrying out the a = a + x operation.

As an aside, the += operator on lists is pretty crazy in its own right. For example, try this sometime:

>>> t = (1, 2, [3, 4], 5)
>>> t[2] += [6,7]
... fails
>>> t
... look at result
>>>