tahsmith
tahsmith

Reputation: 1723

UnboundLocalError when using += on list. Why is `nonlocal` needed here when directly calling __iadd__ works fine?

Consider this code:

def main():
    l = []

    def func():
        l += [1]

    func()
    print(l)

if __name__ == '__main__':
    main()

It will produce:

Traceback (most recent call last):
  File "/Users/tahsmith/Library/Preferences/PyCharm2017.1/scratches/scratch_21.py", line 14, in <module>
    main()
  File "/Users/tahsmith/Library/Preferences/PyCharm2017.1/scratches/scratch_21.py", line 11, in main
    func()
  File "/Users/tahsmith/Library/Preferences/PyCharm2017.1/scratches/scratch_21.py", line 9, in func
    l += [1]
UnboundLocalError: local variable 'l' referenced before assignment

This itself can be fixed by either using nonlocal l at the start of func or using __iadd__ directly instead of +=.

Question: Why is nonlocal needed here?

This is very surprising to me.

Upvotes: 0

Views: 74

Answers (2)

Martijn Pieters
Martijn Pieters

Reputation: 1122392

+= is the augmented assignment operator; it roughly translates to:

def func():
    l = l + [1]

If you were to replace l += [1] with a call to object.__iadd__(), you can't ignore the return value of that call if you were to use it properly:

def func():
    l = l.__iadd__([1])

Both of those translations also need a nonlocal statement, because both access l and assign back to l.

You can get away with ignoring the return value of object.__iadd__ because list objects are mutable; the list is mutated in-place. But then you may as well use the list.extend() call in that case:

def func():
    l.extend([1])

list.__iadd__(), under the covers, calls list.extend() before returning self.

Upvotes: 3

Dimitris Fasarakis Hilliard
Dimitris Fasarakis Hilliard

Reputation: 160467

Because, under the covers, l += [1] leads to:

l = l + [1]

which references the name l before that's assigned to; that's why you'll get the UnboundLocalError.

l.__iadd__, on the other hand, is a simple function call; it doesn't perform an assignment thereby negating the need for nonlocal to assist in where to look for the name l.

Upvotes: 1

Related Questions