Globalize lonely augmented assignment

Mark Dickinson suggested discussion take place here, rather than at bugs.python http://bugs.python.org/issue8977 To note, his counter example would currently raise an UnboundLocalError _________________________________________________________________ Jeux Messenger : mettez vos amis au défi! http://go.microsoft.com/?linkid=9734397

Hello list I’m really uncomfortable with your proposal too. FTR, let me copy it: You want this code to work: A = [1, 2, 3] def f(x): A += [x] f(4) # appends 4 to A It fails today to be consistent with this equivalent code: def f(x): B = A + [x] A = B I can’t see one way working but not the other.
To note, his counter example would currently raise an UnboundLocalError
It’s not an alternate example that would work today, but a consequence of your proposal that we definitely don’t want (mutating immutable objects). To add a third reason to reject your patch: f = A.append # :) Regards

On 12/06/10 07:06, Fake Name wrote:
For those not checking the issue discussion, I'll note that Guido's opinion is that this is a borderline case. Either behaviour (mutate/rebind the global or raise UnboundLocalError) is going to be confusing in some cases and intuitive in others. To quote Mark: """ I guess there's a mismatch either way around: currently, "A += [4]" and "A.append(4)" behave differently for (e.g.,) a list A. With the proposed change, "n += 3" and "n = n + 3" behave differently for a integer n.""" I agree with both of those points. However, I believe this is a case where the cognitive cost of changing the status quo isn't worthwhile. New Python users can be taught very quickly that assigning to a variable from a different scope requires a global or nonlocal declaration. While existing users could be taught easily enough that that was no longer necessary for augmented assignment, how much real world code would actually benefit? (now, Paul Graham's accumulator hobby horse doesn't count as real world code) Call it a -0. I'm not implacably opposed, I just don't see it as a good use of developer (and documentation author!) time. Cheers, Nick. P.S. Any such change would have to wait until after the moratorium anyway. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------

I believe it would be simpler to learn that variables are _only_ local if bound with the assignment operator. I view the augmented assignment operators as different beasts. This patch doesn't quite meet its goals in that respect. I'd like to further the locality of a variable to "A variable is local if, and only if, it is first referenced as the left hand side of an assignment on all code paths." This patch fails to set that rule For example, def f(x): a.append(x) if len(a)>5:a=[5] If a is bound as a local, this throws an UnboundLocalError. Why then is it not set to try the global namespace, that place we cannot be so certain of the exception in? It comes down to the view of UnboundLocalError as a feature or a bug _________________________________________________________________ Occupez vos temps morts avec les jeux Messenger! http://go.microsoft.com/?linkid=9734395

Demur Rumed wrote:
I believe it would be simpler to learn that variables are _only_ local if bound with the assignment operator.
I'd be happy with that, but
is a *much* more complicated rule both to learn and comply with. What happens if this is true for some code paths but not others? That would probably indicate a mistake, and having the scope default to global in that case would be quite dangerous. For the matter at hand, a compromise might be to raise a compile-time error if a variable is referenced by an augmented assignment without either a global declaration or a plain assignment to the same variable somewhere in the function. That would help newbies by explaining more clearly what the problem is, while not affecting any existing correct code. -- Greg

On 6/11/2010 9:18 PM, Demur Rumed wrote:
I believe it would be simpler to learn that variables are _only_ local if bound with the assignment operator.
Assignment is NOT NOT NOT an operator. It is a type of statement.
I view the augmented assignment operators as different beasts.
Your view is one that leads to buggy code. It is wrong in that respect. An augmented assignment STATEMEMT is both a STATEMENT, not an operator, and an ASSIGNMENT statement. Misunderstanding this leads to buggy code and posts on python list "why doesnt my code not work righ?" -1+ on the proposal as it will lead to confusion and bugs. Terry Jan Reedy

On Sat, Jun 12, 2010 at 3:10 AM, Terry Reedy <tjreedy@udel.edu> wrote:
I am curious about these buggy code examples. Do you have any? The standard assignment statement _binds_ the local variable. But the augmented assignment only _rebinds_ it. The augmented assignment does not give the variable a value if it doesn't already have one. I think that we all agree that if the function has an assignment to the variable some place else, the variable is a local variable. So we are considering the case where no assignment to the variable exists within the function, but there is an augmented assignment. But in this case, if we say that the variable is a local variable, how did this local variable get a value that the augmented assignment can then use? The only way that I can think of is: def foo(): def bar(): nonlocal A A = [] bar() A += [2] What am I missing? -Bruce

Am 12.06.2010 17:43, schrieb Bruce Frederiksen:
(I assume you intended a global A somewhere outside of foo().) With the proposed semantics, this would actually be a compilation error, since there is no nonlocal binding of A that the "nonlocal" statement could bring into scope.
What am I missing?
Currently, augmented assignment has a very straightforward translation to plain assignment:: a += b is equivalent to a = a.__iadd__(b) (Not just ``a.__iadd__(b)``, as some people think at first. That's a problem, see below.) With the proposal, it would be much more complicated and dependent on the context: "... it's the same as <code>, but if the name would only be made a local by augmented assignment statements, it's automatically made nonlocal if there's a matching non-local binding, or global otherwise." Pretty scary. And while I think about it, it's pretty implicit as well, since it basically makes a global or nonlocal binding (this would be different if the translation didn't include the actual assignment). Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

On Sat, Jun 12, 2010 at 2:28 PM, Georg Brandl <g.brandl@gmx.net> wrote:
No, I didn't intend a global A, and yes, this would be a compilation error under the proposed semantics. What I meant was that the current semantics are broken. I think that it could be argued that it doesn't make sense to have augmented assignment cause a variable to be made local; because, outside of my example above, execution of the augmented assignment would _always_ produce an UnboundLocalError. And that's because the augmented assignment also refers to the variable _before_ setting it. So, to be a legal program (ie, one that doesn't raise UnboundLocalError), there _must_ be some other assignment to the variable in the function. And if so, this other assignment would cause the variable to be made local, and the augmented assignment is immaterial to this decision. So my challenge (to see if I'm overlooking something) is to show me a current Python program that only uses augmented assignment to cause a variable to be made local, but does not raise UnboundLocalError when the augmented assignment is run. If these examples don't exist, it sounds like this is a bug in the current language design. def foo(): # this causes 'a' to be made local; both current and proposed. a = 5 # so 'a' here is local; both current and proposed. a += 7 def foo(): # without an assignment to 'a', this is currently always an error! # it can only make sense if 'a' is global! a += 7 If you can't do that, then this is a bug!
But this can't be treated as a simple macro expansion inside the compiler, as that would cause the lhs to be evaluated twice. For example in: a[fn_with_side_effects()] += b
I agree completely that anything more complicated than striking the augmented assignment from the list of statements that cause a variable to be made local is scary. It should not depend on what global bindings are present at this or that time, or whether some other local assignment has or has not been executed prior to the augmented assignment. The augmented assignment statement should simply not be a factor in the decision to make a variable local, and having it be a factor is a bug in the current language design as it can never lead to legal programs (at least before the addition of the "nonlocal" statement), only misbehaving ones. -Bruce

Am 12.06.2010 21:40, schrieb Bruce Frederiksen:
Yes, but why does that make the current semantics broken? Is it broken if you do this:: def foo(): print a a = 1 I would rather say it's a programming error. (It could be argued that the compiler should warn about it; I would be in favor of that, if it weren't impossible to implement correctly for all cases.)
Yes, it is a bug -- a bug in your code. I don't understand your reasoning here. Just because you can't use a construct under some circumstances, its semantics are buggy? Does division have a bug because you can't divide by zero?
That's true, but it's also unimportant and a special case.
But by "striking" the augassign from that list, you *are* making it that complicated. As I explained, augmented assignment *does* contain an assignment, so some namespace must be determined where to assign. That gives you the complication, because assignment is always local in Python, except if you explicitly put a global or nonlocal statement.
Again, I can see no bug in language design just because you can't use every construct in every context. Nobody forces you to write functions with only an augassign in them. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

Georg Brandl wrote:
Yes, but why does that make the current semantics broken?
The current semantics perversely make certain code useless that would otherwise have an obvious and useful interpretation. Maybe "suboptimal" would be a better adjective.
But if the programmer intended a to be global, the *only* reason it's a bug is the current somewhat arbitrary interpretation placed on the augmented assignment. -- Greg

Am 13.06.2010 02:49, schrieb Greg Ewing:
While introducing a special case.
Maybe "suboptimal" would be a better adjective.
Yes, I wouldn't argue against that, since it allows for subjectiveness :)
Hmm, I would call it consistent rather than arbitrary. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

Georg a=[1,2,3] def f(x):a[x]=x f(0) Some like to think of []= as a form of augmented assignment Currently, []= doesn't align with other augmenteds on this point That doesn't seem very consistent. Add on that augmented assignment is the only globalizing store statement which also dereferences, and consistency doesn't seem to be a strong point against this proposal _________________________________________________________________ Regardez-moi dans les yeux : vidéobavardage GRATUIT http://go.microsoft.com/?linkid=9734396

Georg Brandl wrote:
All language design decisions are subjective. (If they weren't, there would be no decision to make.)
Hmm, I would call it consistent rather than arbitrary.
But it's a foolish consistency, IMO. It makes the description in the manual about half a sentence shorter, at the expense of semantics that are unintuitive and useless. -- Greg

Georg Brandl wrote:
a += b is equivalent to a = a.__iadd__(b)
It's not quite the same as that, because if a stands for an indexed expression, the index is only evaluated once.
It doesn't have to be as complicated as that -- you only need to add something like "except that if the LHS is a bare name, it does not imply that the name is local." Any context-dependent effects then follow from the existing scope rules. -- Greg

Am 13.06.2010 02:19, schrieb Greg Ewing:
They don't -- as I said in the other mail, there is no "existing scope rule" that covers assignments that are implicitly global or nonlocal. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

Georg Brandl wrote:
They don't -- as I said in the other mail, there is no "existing scope rule" that covers assignments that are implicitly global or nonlocal.
I don't see how you come to that conclusion. You just need to disregard the augmented assignment and follow the normal rules based on the presence of plain assignments and global and nonlocal declarations. -- Greg

On 14/06/10 10:43, Greg Ewing wrote:
What we would actually be doing is going from "augmented assignment determines the scope to assign to based on the same rules as normal assignment" to "augmented assignment determines the scope to assign to based on the same rules as variable referencing" (i.e. wherever we find the value when looking it up on the right hand side, we would put it back in the same place). The semantics aren't the problem here - they can be made perfectly clear and reasonable. The only question is whether they are *sufficiently* useful to justify the effort involved in getting from the status quo to new (more sensible) semantics. Keep in mind, that effort is a lot more than just a patch to CPython to fix our implementation, unit tests and documentation. There's also: - doing the same thing for other implementations (e.g. Jython, IronPython, PyPy) - impact on CPython branches/forks (e.g. Unladen Swallow) - impact on Python-like languages (e.g. Cython) - updating assorted non-PSF documentation (including books) - updating training materials It's for exactly these reasons that the language moratorium has been put in place: so we can't even be *tempted* by this kind of change until everyone has had a chance to at least catch up to the 3.2 state of the world. So, for this suggestion to go any further, it will need: - a PEP (one that acknowledges this is a post-moratorium change) - solid examples of real-world code that would be improved by this (e.g. from the standard library, or from major third party Python applications) Since the most this will save anyone is the occasional global or nonlocal statement, I suspect the second point is going to being a difficult bar to achieve. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------

On 6/12/2010 11:43 AM, Bruce Frederiksen wrote:
Yes. Think a bit, or search the python-list archives, where I have been answering newbie questions and confusions for a decade.
And this proposal would break that simple rule. It also would break the simple rule than one can only rebind a global or nonlocal name if one explicit declares them. Namespaces are complex enough that any simplicity is a virtue.
So we are considering the case where no assignment to the variable exists within the function, but there is an augmented assignment.
This *is* an assignment, documented in 6.2.1. Augmented assignment statements as a subsection of 6.2. Assignment statements. There is says "the assignment done by augmented assignment statements is handled the same way as normal assignments." This proposal would add a fiddly exception. If there is no previous assignment, it is a bug and should be flagged. To expand on what Georg said, x op= y is equivalent to something like <ref> '=' target('x') <ref> = *<ref> iop y where <ref> and target('x') are interpreter-level reference constructs, '=' is internal, interpreter-level binding, and *<ref> is the Pythoh object <ref> references. This proposal would break that equivalence. It would also make the meaning of x op= y depend on what other statememts (other than the exceptional global/nonlocal declarations) are present in the same block. Consider: x = 1 ... def f(y,z) x = y+z ... x /= 2.0 runs fine. Now during editing/refactoring, the x=y+z line is removed or x is change to something else. The program has a bug and an error should be raised. This proposal would mask the bug and have the x /= 2.0 statement change its meaning. Now, one can tell what global vars a function rebinds by looking for a global statement, which sensible programmers always put at the top of the function body after any doc string. (I think that someone argued that "well, globals can be mutated without declaration" as if compounding a somewhat bad thing is a good thing. As a matter style, I think declaring a global declaration for non-args mutated by a function, when possible, would also be a good thing. Or the doc string should mention such.) In summary, this proposal creates several problems, all for the sake of a programmer who does not want to type 'global x'. -10.

Terry Reedy wrote:
An augmented assignment STATEMEMT is both a STATEMENT, not an operator, and an ASSIGNMENT statement.
This is just at statement of the way things are, not an argument for keeping them that way.
-1+ on the proposal as it will lead to confusion and bugs.
I don't see how it would lead to substantially greater chance of bugs than there currently is for other cases where you reference an intended local without assigning to it. If you do that in any other way, it gets looked up as a global, which almost certainly results in a NameError. If the name happens to clash with an existing global, then something more obscure happens, but that's relatively rare. It seems to me that getting an error message about a global name in this case would be less confusing. The thought process would then be "Global? Eh? But it's supposed to be local! Oh, I see, I haven't initialised it, how silly of me." Whereas currently it's "Local? It's supposed to be global, why the heck does the stupid interpreter think it's local?" <Stares at code for 3.7 hours and then posts a message on c.l.py.> -- Greg

On 12/06/10 11:18, Demur Rumed wrote:
The only thing even *remotely* on the table here is to take augmented assignment out of the list of statements that will create a new local variable. For 3.x, that list is currently: - assignment (i.e. '=') - augmented assignment (i.e. '+=', '*=', etc) - function/generator definitions (i.e. def)* - class definitions - for loops - try/except statements - import statements - with statements *Unlike other statements in this list, def statements can affect two different scopes. The defined name becomes a local in the scope containing the statement, while the names of any declared parameters become local inside the statement's own scope. The compiler identifies local variables via static analysis of the function as a whole to see if they are used as name binding targets in any of the above statements *anywhere* in the function. We are *not* going to change that, not just because doing anything else would be far to error-prone, but also because any other interpretation would make compilation far too difficult. For example, consider the following example: def f(x): if randint(2): a = [5] return a[x] # Emit code for global or local lookup? The compiler has to choose to emit a global or local lookup opcode at compile time - it doesn't have the luxury of knowing whether or not the name binding statement will actually be executed at runtime, so it ignores any conditional execution when deciding whether or not a name is bound locally. UnboundLocalError then covers all cases where you attempt to use a local variable name before you have bound it to something. Now, as to the reason we can even consider taking augmented assignment out of the list above: of the current name binding statements, it is the *only* one which requires that a referenced name already be bound using one of the *other* statements in the list. If augmented assignment is currently used in a function *without* raising UnboundLocalError at runtime, then that can only be because the target has been bound by other means, either in the current scope, or else in a different scope and then explicitly declared as coming from another scope via a global or nonlocal statement. So, without breaking existing code (that wasn't already broken), we could change the default scope for augmented assignment from "always use the local scope" to be: - if a name is declared local by other means, treat it is local - it the name exists in a surrounding scope, treat it as nonlocal - otherwise treat it as global That would almost certainly be more useful than the current behaviour. The question is whether it is *sufficiently* useful to justify the effort in updating the documentation and implementing this not just for CPython, but for other VMs such as Jython, IronPython and PyPy. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------

On Fri, 11 Jun 2010 21:18:48 -0400 Demur Rumed <junkmute@hotmail.com> wrote:
Certainly a feature. In case of ambiguity, a variable should be considered local rather than global. It makes the language much safer. It's also why I'm -1 on your proposal. Regards Antoine.

Hello list I’m really uncomfortable with your proposal too. FTR, let me copy it: You want this code to work: A = [1, 2, 3] def f(x): A += [x] f(4) # appends 4 to A It fails today to be consistent with this equivalent code: def f(x): B = A + [x] A = B I can’t see one way working but not the other.
To note, his counter example would currently raise an UnboundLocalError
It’s not an alternate example that would work today, but a consequence of your proposal that we definitely don’t want (mutating immutable objects). To add a third reason to reject your patch: f = A.append # :) Regards

On 12/06/10 07:06, Fake Name wrote:
For those not checking the issue discussion, I'll note that Guido's opinion is that this is a borderline case. Either behaviour (mutate/rebind the global or raise UnboundLocalError) is going to be confusing in some cases and intuitive in others. To quote Mark: """ I guess there's a mismatch either way around: currently, "A += [4]" and "A.append(4)" behave differently for (e.g.,) a list A. With the proposed change, "n += 3" and "n = n + 3" behave differently for a integer n.""" I agree with both of those points. However, I believe this is a case where the cognitive cost of changing the status quo isn't worthwhile. New Python users can be taught very quickly that assigning to a variable from a different scope requires a global or nonlocal declaration. While existing users could be taught easily enough that that was no longer necessary for augmented assignment, how much real world code would actually benefit? (now, Paul Graham's accumulator hobby horse doesn't count as real world code) Call it a -0. I'm not implacably opposed, I just don't see it as a good use of developer (and documentation author!) time. Cheers, Nick. P.S. Any such change would have to wait until after the moratorium anyway. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------

I believe it would be simpler to learn that variables are _only_ local if bound with the assignment operator. I view the augmented assignment operators as different beasts. This patch doesn't quite meet its goals in that respect. I'd like to further the locality of a variable to "A variable is local if, and only if, it is first referenced as the left hand side of an assignment on all code paths." This patch fails to set that rule For example, def f(x): a.append(x) if len(a)>5:a=[5] If a is bound as a local, this throws an UnboundLocalError. Why then is it not set to try the global namespace, that place we cannot be so certain of the exception in? It comes down to the view of UnboundLocalError as a feature or a bug _________________________________________________________________ Occupez vos temps morts avec les jeux Messenger! http://go.microsoft.com/?linkid=9734395

Demur Rumed wrote:
I believe it would be simpler to learn that variables are _only_ local if bound with the assignment operator.
I'd be happy with that, but
is a *much* more complicated rule both to learn and comply with. What happens if this is true for some code paths but not others? That would probably indicate a mistake, and having the scope default to global in that case would be quite dangerous. For the matter at hand, a compromise might be to raise a compile-time error if a variable is referenced by an augmented assignment without either a global declaration or a plain assignment to the same variable somewhere in the function. That would help newbies by explaining more clearly what the problem is, while not affecting any existing correct code. -- Greg

On 6/11/2010 9:18 PM, Demur Rumed wrote:
I believe it would be simpler to learn that variables are _only_ local if bound with the assignment operator.
Assignment is NOT NOT NOT an operator. It is a type of statement.
I view the augmented assignment operators as different beasts.
Your view is one that leads to buggy code. It is wrong in that respect. An augmented assignment STATEMEMT is both a STATEMENT, not an operator, and an ASSIGNMENT statement. Misunderstanding this leads to buggy code and posts on python list "why doesnt my code not work righ?" -1+ on the proposal as it will lead to confusion and bugs. Terry Jan Reedy

On Sat, Jun 12, 2010 at 3:10 AM, Terry Reedy <tjreedy@udel.edu> wrote:
I am curious about these buggy code examples. Do you have any? The standard assignment statement _binds_ the local variable. But the augmented assignment only _rebinds_ it. The augmented assignment does not give the variable a value if it doesn't already have one. I think that we all agree that if the function has an assignment to the variable some place else, the variable is a local variable. So we are considering the case where no assignment to the variable exists within the function, but there is an augmented assignment. But in this case, if we say that the variable is a local variable, how did this local variable get a value that the augmented assignment can then use? The only way that I can think of is: def foo(): def bar(): nonlocal A A = [] bar() A += [2] What am I missing? -Bruce

Am 12.06.2010 17:43, schrieb Bruce Frederiksen:
(I assume you intended a global A somewhere outside of foo().) With the proposed semantics, this would actually be a compilation error, since there is no nonlocal binding of A that the "nonlocal" statement could bring into scope.
What am I missing?
Currently, augmented assignment has a very straightforward translation to plain assignment:: a += b is equivalent to a = a.__iadd__(b) (Not just ``a.__iadd__(b)``, as some people think at first. That's a problem, see below.) With the proposal, it would be much more complicated and dependent on the context: "... it's the same as <code>, but if the name would only be made a local by augmented assignment statements, it's automatically made nonlocal if there's a matching non-local binding, or global otherwise." Pretty scary. And while I think about it, it's pretty implicit as well, since it basically makes a global or nonlocal binding (this would be different if the translation didn't include the actual assignment). Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

On Sat, Jun 12, 2010 at 2:28 PM, Georg Brandl <g.brandl@gmx.net> wrote:
No, I didn't intend a global A, and yes, this would be a compilation error under the proposed semantics. What I meant was that the current semantics are broken. I think that it could be argued that it doesn't make sense to have augmented assignment cause a variable to be made local; because, outside of my example above, execution of the augmented assignment would _always_ produce an UnboundLocalError. And that's because the augmented assignment also refers to the variable _before_ setting it. So, to be a legal program (ie, one that doesn't raise UnboundLocalError), there _must_ be some other assignment to the variable in the function. And if so, this other assignment would cause the variable to be made local, and the augmented assignment is immaterial to this decision. So my challenge (to see if I'm overlooking something) is to show me a current Python program that only uses augmented assignment to cause a variable to be made local, but does not raise UnboundLocalError when the augmented assignment is run. If these examples don't exist, it sounds like this is a bug in the current language design. def foo(): # this causes 'a' to be made local; both current and proposed. a = 5 # so 'a' here is local; both current and proposed. a += 7 def foo(): # without an assignment to 'a', this is currently always an error! # it can only make sense if 'a' is global! a += 7 If you can't do that, then this is a bug!
But this can't be treated as a simple macro expansion inside the compiler, as that would cause the lhs to be evaluated twice. For example in: a[fn_with_side_effects()] += b
I agree completely that anything more complicated than striking the augmented assignment from the list of statements that cause a variable to be made local is scary. It should not depend on what global bindings are present at this or that time, or whether some other local assignment has or has not been executed prior to the augmented assignment. The augmented assignment statement should simply not be a factor in the decision to make a variable local, and having it be a factor is a bug in the current language design as it can never lead to legal programs (at least before the addition of the "nonlocal" statement), only misbehaving ones. -Bruce

Am 12.06.2010 21:40, schrieb Bruce Frederiksen:
Yes, but why does that make the current semantics broken? Is it broken if you do this:: def foo(): print a a = 1 I would rather say it's a programming error. (It could be argued that the compiler should warn about it; I would be in favor of that, if it weren't impossible to implement correctly for all cases.)
Yes, it is a bug -- a bug in your code. I don't understand your reasoning here. Just because you can't use a construct under some circumstances, its semantics are buggy? Does division have a bug because you can't divide by zero?
That's true, but it's also unimportant and a special case.
But by "striking" the augassign from that list, you *are* making it that complicated. As I explained, augmented assignment *does* contain an assignment, so some namespace must be determined where to assign. That gives you the complication, because assignment is always local in Python, except if you explicitly put a global or nonlocal statement.
Again, I can see no bug in language design just because you can't use every construct in every context. Nobody forces you to write functions with only an augassign in them. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

Georg Brandl wrote:
Yes, but why does that make the current semantics broken?
The current semantics perversely make certain code useless that would otherwise have an obvious and useful interpretation. Maybe "suboptimal" would be a better adjective.
But if the programmer intended a to be global, the *only* reason it's a bug is the current somewhat arbitrary interpretation placed on the augmented assignment. -- Greg

Am 13.06.2010 02:49, schrieb Greg Ewing:
While introducing a special case.
Maybe "suboptimal" would be a better adjective.
Yes, I wouldn't argue against that, since it allows for subjectiveness :)
Hmm, I would call it consistent rather than arbitrary. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

Georg a=[1,2,3] def f(x):a[x]=x f(0) Some like to think of []= as a form of augmented assignment Currently, []= doesn't align with other augmenteds on this point That doesn't seem very consistent. Add on that augmented assignment is the only globalizing store statement which also dereferences, and consistency doesn't seem to be a strong point against this proposal _________________________________________________________________ Regardez-moi dans les yeux : vidéobavardage GRATUIT http://go.microsoft.com/?linkid=9734396

Georg Brandl wrote:
All language design decisions are subjective. (If they weren't, there would be no decision to make.)
Hmm, I would call it consistent rather than arbitrary.
But it's a foolish consistency, IMO. It makes the description in the manual about half a sentence shorter, at the expense of semantics that are unintuitive and useless. -- Greg

Georg Brandl wrote:
a += b is equivalent to a = a.__iadd__(b)
It's not quite the same as that, because if a stands for an indexed expression, the index is only evaluated once.
It doesn't have to be as complicated as that -- you only need to add something like "except that if the LHS is a bare name, it does not imply that the name is local." Any context-dependent effects then follow from the existing scope rules. -- Greg

Am 13.06.2010 02:19, schrieb Greg Ewing:
They don't -- as I said in the other mail, there is no "existing scope rule" that covers assignments that are implicitly global or nonlocal. Georg -- Thus spake the Lord: Thou shalt indent with four spaces. No more, no less. Four shall be the number of spaces thou shalt indent, and the number of thy indenting shall be four. Eight shalt thou not indent, nor either indent thou two, excepting that thou then proceed to four. Tabs are right out.

Georg Brandl wrote:
They don't -- as I said in the other mail, there is no "existing scope rule" that covers assignments that are implicitly global or nonlocal.
I don't see how you come to that conclusion. You just need to disregard the augmented assignment and follow the normal rules based on the presence of plain assignments and global and nonlocal declarations. -- Greg

On 14/06/10 10:43, Greg Ewing wrote:
What we would actually be doing is going from "augmented assignment determines the scope to assign to based on the same rules as normal assignment" to "augmented assignment determines the scope to assign to based on the same rules as variable referencing" (i.e. wherever we find the value when looking it up on the right hand side, we would put it back in the same place). The semantics aren't the problem here - they can be made perfectly clear and reasonable. The only question is whether they are *sufficiently* useful to justify the effort involved in getting from the status quo to new (more sensible) semantics. Keep in mind, that effort is a lot more than just a patch to CPython to fix our implementation, unit tests and documentation. There's also: - doing the same thing for other implementations (e.g. Jython, IronPython, PyPy) - impact on CPython branches/forks (e.g. Unladen Swallow) - impact on Python-like languages (e.g. Cython) - updating assorted non-PSF documentation (including books) - updating training materials It's for exactly these reasons that the language moratorium has been put in place: so we can't even be *tempted* by this kind of change until everyone has had a chance to at least catch up to the 3.2 state of the world. So, for this suggestion to go any further, it will need: - a PEP (one that acknowledges this is a post-moratorium change) - solid examples of real-world code that would be improved by this (e.g. from the standard library, or from major third party Python applications) Since the most this will save anyone is the occasional global or nonlocal statement, I suspect the second point is going to being a difficult bar to achieve. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------

On 6/12/2010 11:43 AM, Bruce Frederiksen wrote:
Yes. Think a bit, or search the python-list archives, where I have been answering newbie questions and confusions for a decade.
And this proposal would break that simple rule. It also would break the simple rule than one can only rebind a global or nonlocal name if one explicit declares them. Namespaces are complex enough that any simplicity is a virtue.
So we are considering the case where no assignment to the variable exists within the function, but there is an augmented assignment.
This *is* an assignment, documented in 6.2.1. Augmented assignment statements as a subsection of 6.2. Assignment statements. There is says "the assignment done by augmented assignment statements is handled the same way as normal assignments." This proposal would add a fiddly exception. If there is no previous assignment, it is a bug and should be flagged. To expand on what Georg said, x op= y is equivalent to something like <ref> '=' target('x') <ref> = *<ref> iop y where <ref> and target('x') are interpreter-level reference constructs, '=' is internal, interpreter-level binding, and *<ref> is the Pythoh object <ref> references. This proposal would break that equivalence. It would also make the meaning of x op= y depend on what other statememts (other than the exceptional global/nonlocal declarations) are present in the same block. Consider: x = 1 ... def f(y,z) x = y+z ... x /= 2.0 runs fine. Now during editing/refactoring, the x=y+z line is removed or x is change to something else. The program has a bug and an error should be raised. This proposal would mask the bug and have the x /= 2.0 statement change its meaning. Now, one can tell what global vars a function rebinds by looking for a global statement, which sensible programmers always put at the top of the function body after any doc string. (I think that someone argued that "well, globals can be mutated without declaration" as if compounding a somewhat bad thing is a good thing. As a matter style, I think declaring a global declaration for non-args mutated by a function, when possible, would also be a good thing. Or the doc string should mention such.) In summary, this proposal creates several problems, all for the sake of a programmer who does not want to type 'global x'. -10.

Terry Reedy wrote:
An augmented assignment STATEMEMT is both a STATEMENT, not an operator, and an ASSIGNMENT statement.
This is just at statement of the way things are, not an argument for keeping them that way.
-1+ on the proposal as it will lead to confusion and bugs.
I don't see how it would lead to substantially greater chance of bugs than there currently is for other cases where you reference an intended local without assigning to it. If you do that in any other way, it gets looked up as a global, which almost certainly results in a NameError. If the name happens to clash with an existing global, then something more obscure happens, but that's relatively rare. It seems to me that getting an error message about a global name in this case would be less confusing. The thought process would then be "Global? Eh? But it's supposed to be local! Oh, I see, I haven't initialised it, how silly of me." Whereas currently it's "Local? It's supposed to be global, why the heck does the stupid interpreter think it's local?" <Stares at code for 3.7 hours and then posts a message on c.l.py.> -- Greg

On 12/06/10 11:18, Demur Rumed wrote:
The only thing even *remotely* on the table here is to take augmented assignment out of the list of statements that will create a new local variable. For 3.x, that list is currently: - assignment (i.e. '=') - augmented assignment (i.e. '+=', '*=', etc) - function/generator definitions (i.e. def)* - class definitions - for loops - try/except statements - import statements - with statements *Unlike other statements in this list, def statements can affect two different scopes. The defined name becomes a local in the scope containing the statement, while the names of any declared parameters become local inside the statement's own scope. The compiler identifies local variables via static analysis of the function as a whole to see if they are used as name binding targets in any of the above statements *anywhere* in the function. We are *not* going to change that, not just because doing anything else would be far to error-prone, but also because any other interpretation would make compilation far too difficult. For example, consider the following example: def f(x): if randint(2): a = [5] return a[x] # Emit code for global or local lookup? The compiler has to choose to emit a global or local lookup opcode at compile time - it doesn't have the luxury of knowing whether or not the name binding statement will actually be executed at runtime, so it ignores any conditional execution when deciding whether or not a name is bound locally. UnboundLocalError then covers all cases where you attempt to use a local variable name before you have bound it to something. Now, as to the reason we can even consider taking augmented assignment out of the list above: of the current name binding statements, it is the *only* one which requires that a referenced name already be bound using one of the *other* statements in the list. If augmented assignment is currently used in a function *without* raising UnboundLocalError at runtime, then that can only be because the target has been bound by other means, either in the current scope, or else in a different scope and then explicitly declared as coming from another scope via a global or nonlocal statement. So, without breaking existing code (that wasn't already broken), we could change the default scope for augmented assignment from "always use the local scope" to be: - if a name is declared local by other means, treat it is local - it the name exists in a surrounding scope, treat it as nonlocal - otherwise treat it as global That would almost certainly be more useful than the current behaviour. The question is whether it is *sufficiently* useful to justify the effort in updating the documentation and implementing this not just for CPython, but for other VMs such as Jython, IronPython and PyPy. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------

On Fri, 11 Jun 2010 21:18:48 -0400 Demur Rumed <junkmute@hotmail.com> wrote:
Certainly a feature. In case of ambiguity, a variable should be considered local rather than global. It makes the language much safer. It's also why I'm -1 on your proposal. Regards Antoine.
participants (10)
-
Antoine Pitrou
-
Bruce Frederiksen
-
Cesare Di Mauro
-
Demur Rumed
-
Fake Name
-
Georg Brandl
-
Greg Ewing
-
Nick Coghlan
-
Terry Reedy
-
Éric Araujo