We all know that the following code won't work because of UnboundLocalError and that to get around it, one needs to use nonlocal:<div><br></div><div><div>>>> def accum():</div><div>... <meta charset="utf-8"> x = 0</div>
<div>... <meta charset="utf-8"> def inner():</div><div>... <meta charset="utf-8"> x += 1</div><meta charset="utf-8"><div>... <meta charset="utf-8"> return x</div><meta charset="utf-8"><div>... <meta charset="utf-8"> return inner</div>
<div>... </div><div>>>> inc = accum()</div><div>>>> inc()</div><div>Traceback (most recent call last):</div><div> File "<stdin>", line 1, in <module></div><div> File "<stdin>", line 4, in inner</div>
<div>UnboundLocalError: local variable 'x' referenced before assignment</div><div><br></div><div>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? Compare:</div>
<div><br></div><div>>>> def accum():</div><div>... <meta charset="utf-8"> x = []</div><div>... <meta charset="utf-8"> def inner():</div><div>... <meta charset="utf-8"> x.append(1)</div><meta charset="utf-8"><div>
... <meta charset="utf-8"> return x</div><meta charset="utf-8"><div>... <meta charset="utf-8"> return inner</div><div>... </div><div>>>> inc = accum()</div><div>>>> inc()</div><div>[1]</div><div>
>>> inc()</div><div>[1, 1]</div><div>>>> inc()</div><div>[1, 1, 1]</div><div><br></div><div>So, if I changed += to .append, the code suddenly works fine. Heck, I could also change it to x.__iadd__ if x happens to have that attribute.</div>
<div><br></div><div>As we all know, adding an = anywhere to the function bound will cause x to be considered a local. So, for example, we can make the .append example fail by adding some unreachable code:</div><div><br></div>
<div>>>> def accum():</div><div>... <meta charset="utf-8"> x = []</div><div>... <meta charset="utf-8"> def inner():</div><div>... <meta charset="utf-8"> x.append(1)</div><meta charset="utf-8"><div>... <meta charset="utf-8"> return x</div>
<meta charset="utf-8"><div>... <meta charset="utf-8"> x = 0 #Won't ever be reached, but will cause x to be considered a local</div><meta charset="utf-8"><div>... <meta charset="utf-8"> return inner</div><div>
... </div><div>>>> inc = accum()</div><div>>>> inc()</div><div>Traceback (most recent call last):</div><div> File "<stdin>", line 1, in <module></div><div> File "<stdin>", line 4, in inner</div>
<div>UnboundLocalError: local variable 'x' referenced before assignment</div></div><div><br></div><div>So, my proposal is that += by itself should not cause x to be considered a local variable. There should need to be a normal = assignment for the compiler to count x as a local. If the objection to my proposal is that I'm being "implicit and not explicit" because it would be like there's an implicit "nonlocal," my rebuttal is that we already have "implicit" nonlocals in the case of .append.</div>
<div><br></div><div>-- Carl Johnson</div>