[Python-ideas] Why does += trigger UnboundLocalError?

Paul Svensson paul-python at svensson.org
Wed Jun 1 15:03:40 CEST 2011

On Wed, 1 Jun 2011, Georg Brandl wrote:

> On 01.06.2011 06:52, Carl M. Johnson wrote:
>> We all know that the following code won't work because of UnboundLocalError and
>> that to get around it, one needs to use nonlocal:
>>>>> def accum():
>> ...     x = 0
>> ...     def inner():
>> ...         x += 1
>> ...         return x
>> ...     return inner
>> ...
>>>>> inc = accum()
>>>>> inc()
>> Traceback (most recent call last):
>>   File "<stdin>", line 1, in <module>
>>   File "<stdin>", line 4, in inner
>> UnboundLocalError: local variable 'x' referenced before assignment
>> But why does this happen? Let's think about this a little more closely: += is
>> not the same as =. A += can only happen if the left-hand term was already
>> defined. So, why does the compiler treat this as though there were an assignment
>> inside the function?
> Because x += y is equivalent to
> x = x.__iadd__(y)
> and therefore an assignment is going on here.  Therefore, it's only logical to
> treat it as such when determining scopes.

Off on a bit of a tangent here - this behaviour always bugged me:

 	--> x = ([],)
 	--> x[0] += ['a']
 	Traceback (most recent call last):
 	  File "<stdin>", line 1, in <module>
 	TypeError: 'tuple' object does not support item assignment
 	--> x

I understand by the definition why this happens as it does, but intuitively,
I'd expect an operation to either fail and raise, or succeed and not.

I see two possible ways to make this behave: we can look before we leap,
and raise the exception before calling __iadd__, if the assigment would fail;
or, we can change the definition to only perform the assignment if __iadd__
returns something other than self.

Both these are, to some extent, incompatible language changes.  Both change
how I think about the original proposal: with the first option, it softens
the argument about __iadd__ being called before the assignment, so strengthens
the case for the status quo; with the second option, the definition of
__iadd__ gets more complicated, making me less inclined to dive into this
definition to explain the locality of the assigned variable, preferring it
to be defined separately, and simply.

Back from the tangent, I think Carl's proposal would make Python more
difficult to understand rather than less, so -1 from me.


More information about the Python-ideas mailing list