Why does += trigger UnboundLocalError?

We all know that the following code won't work because of UnboundLocalError and that to get around it, one needs to use nonlocal:
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:
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. 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:
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. -- Carl Johnson

On Tue, May 31, 2011 at 7:48 PM, Georg Brandl <g.brandl@gmx.net> wrote:
But the difference is that you can only use += if the LHS name already exists and is defined. So, it couldn't possibly be referring to a local name if it's the only assignment-like statement within a function body. How could it refer to a local if it has to refer to something that already exists?

On 01.06.2011 08:48, Carl M. Johnson wrote:
Sure, this can only work if the local is assigned somewhere before the augmented assign statement. But this is just like accessing a local before its assignment: in the case of x = 1 def f(): print x x = 2 we also don't treat the first "x" reference as a nonlocal. And the fact remains that augassign *is* an assignment, and the rule is that assignments to out-of-scope names are only allowed when declared using "global" or "nonlocal". Georg

On Tue, May 31, 2011 at 9:05 PM, Georg Brandl <g.brandl@gmx.net> wrote:
I don't think that's a counterexample to the point I'm trying to make. We all agree that if there's an x= somewhere in the function body, then we have to treat the variable as a local. The only possible way around that would be to solve the halting problem in order to figure out if a particular line of code will be reached or not. Agreed, sure, we have to treat the LHS of = as a local. But += is fundamentally different. You cannot have a += statement unless somewhere out there there is a matching = statement. It cannot exist independently. It never works on its own. So, if there is a += statement in the function body and there isn't an = statement in the function body it cannot work. Ever. All function bodies that have a += but no corresponding = or nonlocal are, as of today, broken code. So, if we were to change Python to make += not cause a variable to become a local, it wouldn't change how any (working) Python code today functions (it might causes some tests to change if they were counting on the error). This would be a completely backwards compatible change. Or am I missing something? Is there any scenario where you can get away with using += without = or nonlocal? I guess you could do something with locals().update or the stackframe, but my understanding is that those hacks don't count for language purposes. -- Carl

On 1 June 2011 09:26, Carl M. Johnson <cmjohnson.mailinglist@gmail.com> wrote:
The place to start here is section 4.1 of the language reference (Naming and Binding). Specifically, "A scope defines the visibility of a name within a block. If a local variable is defined in a block, its scope includes that block." Your modification of augmented assignment implies that a block can contain 2 different scopes - consider x = 1 def f(): # The next statement uses the global x x += 1 x = 2 # From here, you have a local x That fundamentally changes the language semantics. If you want to push this change, I'd suggest you start by proposing a change to the language reference section I mentioned above to define your proposed new scoping rules. In my view, that would be sufficiently hard that it'd kill this proposal, but if you can manage to do it, then you may have a chance to get your change accepted. Paul.

I think you missed this statement, even though you quoted it. On 2011-06-01 10:51, Paul Moore wrote:
This means that your example:
Would behave exactly as it does today under the proposed new semantics. Specifically, the "x = 2" statement (and the lack of a nonlocal statement) forces x to be local throughout the function, and the "x += 1" statement then tries to read the local "x" and fails.
That fundamentally changes the language semantics.
I don't think it does. It only makes a difference for functions that contains an augmented assignment to a name without also containing a regular assignment to that name. This case will change from being an error to doing something well-defined and useful. FWIW, I'm +1 on the idea. Best regards - Jacob

My first reaction was: +1 on the proposed change. It seemed logical. Then I had a reservation: it would widen the semantic difference between x += 1 and x = x + 1 which could trip someone innocently making a "trivial" code change from the former to the latter (x unintentionally becomes a local). So how about going further and say that x is only interpreted as local if there is at least one NON-augmented assignment in which x appears as a target on the LHS but x does NOT appear on the RHS? I.e. x = x + 1 (like "x += 1") does not (by itself) make x local. Or is this getting too hard to explain? Best wishes Rob Cliffe

On Wed, Jun 1, 2011 at 12:43 PM, Rob Cliffe <rob.cliffe@btinternet.com> wrote:
I think so; it also has the same disadvantage you mention of getting a semantic change from seemingly neutral changes, but for other changes. For example x = 1 if x == 0 else x-1 would keep x global, but changing it to: if x == 0: x = 1 else: x = x-1 would not do so. -- André Engels, andreengels@gmail.com

Carl M. Johnson wrote:
Agreed, sure, we have to treat the LHS of = as a local. But += is fundamentally different.
No it's not. It is fundamentally the same. Augmented assignment in Python *is* assignment, equivalent to x = x.__iadd__(other). That alone should be enough to kill this proposal stone dead. += is not, except by accident, an in-place addition operator. It is always a re-binding. (Mutable objects are free to mutate in place, if they choose, but the re-binding still takes place.)
Neither does *any* attempt to access an unbound local. Python doesn't, and shouldn't, try to guess what you actually intended so as to make it work. If you want x to refer to a nonlocal, or a global, declare it as such. print x; x = 1 will fail unless there is an earlier x = something. x = x+1 will fail unless there is an earlier x = something. x += 1 will fail unless there is an earlier x = something. Why single out x += 1 for changed semantics to the rule that any assignment makes x a local? What if you don't have a non-local x, should Python guess that you wanted a global? Currently, the rule is simple: any assignment tells the compiler to treat x as local. If you want nonlocal or global, you have to declare it as such. Nice and simple. What actual real-world problem are you trying to solve that you want to change this behaviour? -1 on this change. -- Steven

On Wed, Jun 1, 2011 at 1:21 PM, Steven D'Aprano <steve@pearwood.info> wrote:
The best counter-arguments I've heard so far are Nick's (it would be a pain to go into the guts and change this, and you also need to think about PyPy, Jython, IronPy, etc., etc.) and this one. In terms of "real world problems" this solves, it makes the solution to the Paul Graham language challenge problem (build a function that returns an accumulator) one line shorter. Which is a bit silly, but so far as I can tell, nonlocal was created just to say we have an answer to the Paul Graham question. ;-) I think the benefit of saving that one line is probably outweighed by the brittleness that this would create (ie. changing x += 1 to x = x + 1 could break code), so I withdraw the proposal, at least for now. One additional problem that I ran into is this:
Nonlocal fails at the compilation stage if the variable isn't found. On the other hand, attribute lookup is delayed until runtime, so if by accident you did def f(): count = 0 def g(): cont += 1 #oops typo. return cont return g it's not clear when the function should fail: compile time or runtime. -- Carl

On Thu, Jun 2, 2011 at 3:17 PM, Carl M. Johnson <cmjohnson.mailinglist@gmail.com> wrote:
Nah, nonlocal was added because the introduction of decorators increased the use of closures, and boxing and unboxing variables manually is a PITA. Note that the "translation" of 'x += y' to 'x = x + y' is and always has been a gross oversimplification (albeit a useful one). Reality is complicated by possible provision of __iadd__ by the assignment target, as well as the need to pair up __getitem__/__setitem__ and __getattr__/__setattr__ appropriately when the target is a subscript operation or attribute access. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 01.06.2011 10:26, Carl M. Johnson wrote:
You keep saying that, but I just can't see how += is fundamentally different from =, given its definition as x = x.__iadd__(y). This is a situation that comes up from time to time, where it seems logical to make a change that satisfies "DWIM" feelings, but makes the languge more inconsistent by introducing special cases. This doesn't feel right to me (and the Zen agrees ;) Georg

Carl M. Johnson wrote:
Two problems. Firstly, what error should be raised here? --> def accum(): ... x = 0 ... def inner(): ... x1 += 1 ... return x ... return inner Secondly, the += operator may or may not be a mutating operator depending on the object it's used on: if the object does not have a __iadd__ method, it's not mutating; even if it does have an __iadd__ method, it may not be mutating -- it's up to the object to decide. --> class ex_int(int): ... def __iadd__(self, other): ... return self + other ... --> x = ex_int(7) --> x.__iadd__(3) 10 --> x 7 --> x = [1, 2, 3] --> x.__iadd__([4]) [1, 2, 3, 4] --> x [1, 2, 3, 4] -1 on changing the semantics. ~Ethan~

On Wed, 1 Jun 2011, Georg Brandl wrote:
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 (['a'],) --> 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. /Paul

On Wed, Jun 1, 2011 at 2:52 PM, Carl M. Johnson <cmjohnson.mailinglist@gmail.com> 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:
There's no fundamental reason this couldn't change, but actually changing it simply isn't worth the hassle, so the status quo wins the stalemate. I elaborated further on this point when the topic came up last year: http://mail.python.org/pipermail/python-ideas/2010-June/007448.html Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Nick Coghlan wrote:
Maybe I get to learn something new about Python today. Several times in that thread it was stated that --> a += 1 is a shortcut for --> a.__iadd__(1) It seems to me that this is an implementation detail, and that the actual "longcut" is --> a = a + 1 Likewise, the shortcut of --> some_list[func_with_side_effects()] += some_value is the same as --> index = func_with_side_effects() --> some_list[index] = some_list[index] + some_value Is my understanding correct? ~Ethan~

On Wed, Jun 1, 2011 at 9:56 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
a += 1 is not a shortcut for a.__iadd__(1). It's a shortcut for a = a.__iadd(1). Otherwise this wouldn't work:
Note the difference between these two is one opcode:
--- Bruce Follow me: http://www.twitter.com/Vroo http://www.vroospeak.com Latest tweet: SO disappointed end of the world didn't happen AGAIN! #y2k<http://twitter.com/#!/search?q=%23y2k> #rapture <http://twitter.com/#!/search?q=%23rapture> Now waiting for 2038! #unixrapture <http://twitter.com/#!/search?q=%23unixrapture>

On 6/1/2011 1:32 PM, Bruce Leban wrote:
(In 3.2, one no longer needs to wrap code in a function to dis it. see below.) To see the 'calculate the source/target just once instead of twice' part, you need a source/target that actually requires calculation.
Even this does not show much difference as the dup and rotate substitute for two loads but do not actually save any calculation. However,
The latter has the same dup-rotate in place of a bit more calculation. The same would be true of, for instance, f(a).b. -- Terry Jan Reedy

On 6/1/2011 12:52 AM, Carl M. Johnson wrote:
So, my proposal is that += by itself should not cause x to be considered a local variable.
Right now, 'augmented assigment' is uniformly what it says: an assignment with augmented behavior. 'expr1 op= expr2' is *defined* as being the same as 'expr1 = expr1 op expr2' except that expr1 is evauluated just once*, and if expr1 evaluates to a mutable, the op can be done in place. Some consider the second exception to be a confusing complication and a mistake. Your proposal would require a rewrite of the definition and would add additional complication. Some would then want another exception for when expr1 evaluates to a mutable within an immutable (see Paul Svensson's post). While I do understand your point, I also value uniformity. -1 * It is actually more complicate than than. Expr1 is partially evaluated just once to an internal reference rather than to an object. That reference is then used once to fetch the existing object and once again to rebind to the new or mutated object. Still, it is one behavior for all occurrences. -- Terry Jan Reedy

On 6/1/2011 1:41 PM, Terry Reedy wrote:
While I do understand your point, I also value uniformity. -1
There is another problem I had not thought of before. Right now, Python has (always had) a simple rule: code in a function CANNOT rebind names in outer scopes unless the function has a global or nonlocal declaration. This simple, uniform rule benefits not only the interpreter but human readers. It should not be broken. def f(): 'doc for f' def g(): 'docstring of g' <body of g> <body of f> If g is the only nested function and the body of g does not have a nonlocal declaration (which OUGHT to be at the top if present), then a reader or maintainer of f knows (without reading g in detail) that nothing other that <body of f> can rebind f's locals. -- Terry Jan Reedy

On Tue, May 31, 2011 at 7:48 PM, Georg Brandl <g.brandl@gmx.net> wrote:
But the difference is that you can only use += if the LHS name already exists and is defined. So, it couldn't possibly be referring to a local name if it's the only assignment-like statement within a function body. How could it refer to a local if it has to refer to something that already exists?

On 01.06.2011 08:48, Carl M. Johnson wrote:
Sure, this can only work if the local is assigned somewhere before the augmented assign statement. But this is just like accessing a local before its assignment: in the case of x = 1 def f(): print x x = 2 we also don't treat the first "x" reference as a nonlocal. And the fact remains that augassign *is* an assignment, and the rule is that assignments to out-of-scope names are only allowed when declared using "global" or "nonlocal". Georg

On Tue, May 31, 2011 at 9:05 PM, Georg Brandl <g.brandl@gmx.net> wrote:
I don't think that's a counterexample to the point I'm trying to make. We all agree that if there's an x= somewhere in the function body, then we have to treat the variable as a local. The only possible way around that would be to solve the halting problem in order to figure out if a particular line of code will be reached or not. Agreed, sure, we have to treat the LHS of = as a local. But += is fundamentally different. You cannot have a += statement unless somewhere out there there is a matching = statement. It cannot exist independently. It never works on its own. So, if there is a += statement in the function body and there isn't an = statement in the function body it cannot work. Ever. All function bodies that have a += but no corresponding = or nonlocal are, as of today, broken code. So, if we were to change Python to make += not cause a variable to become a local, it wouldn't change how any (working) Python code today functions (it might causes some tests to change if they were counting on the error). This would be a completely backwards compatible change. Or am I missing something? Is there any scenario where you can get away with using += without = or nonlocal? I guess you could do something with locals().update or the stackframe, but my understanding is that those hacks don't count for language purposes. -- Carl

On 1 June 2011 09:26, Carl M. Johnson <cmjohnson.mailinglist@gmail.com> wrote:
The place to start here is section 4.1 of the language reference (Naming and Binding). Specifically, "A scope defines the visibility of a name within a block. If a local variable is defined in a block, its scope includes that block." Your modification of augmented assignment implies that a block can contain 2 different scopes - consider x = 1 def f(): # The next statement uses the global x x += 1 x = 2 # From here, you have a local x That fundamentally changes the language semantics. If you want to push this change, I'd suggest you start by proposing a change to the language reference section I mentioned above to define your proposed new scoping rules. In my view, that would be sufficiently hard that it'd kill this proposal, but if you can manage to do it, then you may have a chance to get your change accepted. Paul.

I think you missed this statement, even though you quoted it. On 2011-06-01 10:51, Paul Moore wrote:
This means that your example:
Would behave exactly as it does today under the proposed new semantics. Specifically, the "x = 2" statement (and the lack of a nonlocal statement) forces x to be local throughout the function, and the "x += 1" statement then tries to read the local "x" and fails.
That fundamentally changes the language semantics.
I don't think it does. It only makes a difference for functions that contains an augmented assignment to a name without also containing a regular assignment to that name. This case will change from being an error to doing something well-defined and useful. FWIW, I'm +1 on the idea. Best regards - Jacob

My first reaction was: +1 on the proposed change. It seemed logical. Then I had a reservation: it would widen the semantic difference between x += 1 and x = x + 1 which could trip someone innocently making a "trivial" code change from the former to the latter (x unintentionally becomes a local). So how about going further and say that x is only interpreted as local if there is at least one NON-augmented assignment in which x appears as a target on the LHS but x does NOT appear on the RHS? I.e. x = x + 1 (like "x += 1") does not (by itself) make x local. Or is this getting too hard to explain? Best wishes Rob Cliffe

On Wed, Jun 1, 2011 at 12:43 PM, Rob Cliffe <rob.cliffe@btinternet.com> wrote:
I think so; it also has the same disadvantage you mention of getting a semantic change from seemingly neutral changes, but for other changes. For example x = 1 if x == 0 else x-1 would keep x global, but changing it to: if x == 0: x = 1 else: x = x-1 would not do so. -- André Engels, andreengels@gmail.com

Carl M. Johnson wrote:
Agreed, sure, we have to treat the LHS of = as a local. But += is fundamentally different.
No it's not. It is fundamentally the same. Augmented assignment in Python *is* assignment, equivalent to x = x.__iadd__(other). That alone should be enough to kill this proposal stone dead. += is not, except by accident, an in-place addition operator. It is always a re-binding. (Mutable objects are free to mutate in place, if they choose, but the re-binding still takes place.)
Neither does *any* attempt to access an unbound local. Python doesn't, and shouldn't, try to guess what you actually intended so as to make it work. If you want x to refer to a nonlocal, or a global, declare it as such. print x; x = 1 will fail unless there is an earlier x = something. x = x+1 will fail unless there is an earlier x = something. x += 1 will fail unless there is an earlier x = something. Why single out x += 1 for changed semantics to the rule that any assignment makes x a local? What if you don't have a non-local x, should Python guess that you wanted a global? Currently, the rule is simple: any assignment tells the compiler to treat x as local. If you want nonlocal or global, you have to declare it as such. Nice and simple. What actual real-world problem are you trying to solve that you want to change this behaviour? -1 on this change. -- Steven

On Wed, Jun 1, 2011 at 1:21 PM, Steven D'Aprano <steve@pearwood.info> wrote:
The best counter-arguments I've heard so far are Nick's (it would be a pain to go into the guts and change this, and you also need to think about PyPy, Jython, IronPy, etc., etc.) and this one. In terms of "real world problems" this solves, it makes the solution to the Paul Graham language challenge problem (build a function that returns an accumulator) one line shorter. Which is a bit silly, but so far as I can tell, nonlocal was created just to say we have an answer to the Paul Graham question. ;-) I think the benefit of saving that one line is probably outweighed by the brittleness that this would create (ie. changing x += 1 to x = x + 1 could break code), so I withdraw the proposal, at least for now. One additional problem that I ran into is this:
Nonlocal fails at the compilation stage if the variable isn't found. On the other hand, attribute lookup is delayed until runtime, so if by accident you did def f(): count = 0 def g(): cont += 1 #oops typo. return cont return g it's not clear when the function should fail: compile time or runtime. -- Carl

On Thu, Jun 2, 2011 at 3:17 PM, Carl M. Johnson <cmjohnson.mailinglist@gmail.com> wrote:
Nah, nonlocal was added because the introduction of decorators increased the use of closures, and boxing and unboxing variables manually is a PITA. Note that the "translation" of 'x += y' to 'x = x + y' is and always has been a gross oversimplification (albeit a useful one). Reality is complicated by possible provision of __iadd__ by the assignment target, as well as the need to pair up __getitem__/__setitem__ and __getattr__/__setattr__ appropriately when the target is a subscript operation or attribute access. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 01.06.2011 10:26, Carl M. Johnson wrote:
You keep saying that, but I just can't see how += is fundamentally different from =, given its definition as x = x.__iadd__(y). This is a situation that comes up from time to time, where it seems logical to make a change that satisfies "DWIM" feelings, but makes the languge more inconsistent by introducing special cases. This doesn't feel right to me (and the Zen agrees ;) Georg

Carl M. Johnson wrote:
Two problems. Firstly, what error should be raised here? --> def accum(): ... x = 0 ... def inner(): ... x1 += 1 ... return x ... return inner Secondly, the += operator may or may not be a mutating operator depending on the object it's used on: if the object does not have a __iadd__ method, it's not mutating; even if it does have an __iadd__ method, it may not be mutating -- it's up to the object to decide. --> class ex_int(int): ... def __iadd__(self, other): ... return self + other ... --> x = ex_int(7) --> x.__iadd__(3) 10 --> x 7 --> x = [1, 2, 3] --> x.__iadd__([4]) [1, 2, 3, 4] --> x [1, 2, 3, 4] -1 on changing the semantics. ~Ethan~

On Wed, 1 Jun 2011, Georg Brandl wrote:
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 (['a'],) --> 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. /Paul

On Wed, Jun 1, 2011 at 2:52 PM, Carl M. Johnson <cmjohnson.mailinglist@gmail.com> 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:
There's no fundamental reason this couldn't change, but actually changing it simply isn't worth the hassle, so the status quo wins the stalemate. I elaborated further on this point when the topic came up last year: http://mail.python.org/pipermail/python-ideas/2010-June/007448.html Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Nick Coghlan wrote:
Maybe I get to learn something new about Python today. Several times in that thread it was stated that --> a += 1 is a shortcut for --> a.__iadd__(1) It seems to me that this is an implementation detail, and that the actual "longcut" is --> a = a + 1 Likewise, the shortcut of --> some_list[func_with_side_effects()] += some_value is the same as --> index = func_with_side_effects() --> some_list[index] = some_list[index] + some_value Is my understanding correct? ~Ethan~

On Wed, Jun 1, 2011 at 9:56 AM, Ethan Furman <ethan@stoneleaf.us> wrote:
a += 1 is not a shortcut for a.__iadd__(1). It's a shortcut for a = a.__iadd(1). Otherwise this wouldn't work:
Note the difference between these two is one opcode:
--- Bruce Follow me: http://www.twitter.com/Vroo http://www.vroospeak.com Latest tweet: SO disappointed end of the world didn't happen AGAIN! #y2k<http://twitter.com/#!/search?q=%23y2k> #rapture <http://twitter.com/#!/search?q=%23rapture> Now waiting for 2038! #unixrapture <http://twitter.com/#!/search?q=%23unixrapture>

On 6/1/2011 1:32 PM, Bruce Leban wrote:
(In 3.2, one no longer needs to wrap code in a function to dis it. see below.) To see the 'calculate the source/target just once instead of twice' part, you need a source/target that actually requires calculation.
Even this does not show much difference as the dup and rotate substitute for two loads but do not actually save any calculation. However,
The latter has the same dup-rotate in place of a bit more calculation. The same would be true of, for instance, f(a).b. -- Terry Jan Reedy

On 6/1/2011 12:52 AM, Carl M. Johnson wrote:
So, my proposal is that += by itself should not cause x to be considered a local variable.
Right now, 'augmented assigment' is uniformly what it says: an assignment with augmented behavior. 'expr1 op= expr2' is *defined* as being the same as 'expr1 = expr1 op expr2' except that expr1 is evauluated just once*, and if expr1 evaluates to a mutable, the op can be done in place. Some consider the second exception to be a confusing complication and a mistake. Your proposal would require a rewrite of the definition and would add additional complication. Some would then want another exception for when expr1 evaluates to a mutable within an immutable (see Paul Svensson's post). While I do understand your point, I also value uniformity. -1 * It is actually more complicate than than. Expr1 is partially evaluated just once to an internal reference rather than to an object. That reference is then used once to fetch the existing object and once again to rebind to the new or mutated object. Still, it is one behavior for all occurrences. -- Terry Jan Reedy

On 6/1/2011 1:41 PM, Terry Reedy wrote:
While I do understand your point, I also value uniformity. -1
There is another problem I had not thought of before. Right now, Python has (always had) a simple rule: code in a function CANNOT rebind names in outer scopes unless the function has a global or nonlocal declaration. This simple, uniform rule benefits not only the interpreter but human readers. It should not be broken. def f(): 'doc for f' def g(): 'docstring of g' <body of g> <body of f> If g is the only nested function and the body of g does not have a nonlocal declaration (which OUGHT to be at the top if present), then a reader or maintainer of f knows (without reading g in detail) that nothing other that <body of f> can rebind f's locals. -- Terry Jan Reedy
participants (13)
-
Andre Engels
-
andrew cooke
-
Bruce Leban
-
Carl M. Johnson
-
Ethan Furman
-
Georg Brandl
-
Jacob Holm
-
Nick Coghlan
-
Paul Moore
-
Paul Svensson
-
Rob Cliffe
-
Steven D'Aprano
-
Terry Reedy