
My slight preference for leaving += and friends alone is that a function using them to rebind nonlocals would be hard to read, that since the change only applies when the LHS is a bare name the important use cases for augmented assignment don't apply any way, that it's a bit subtle to explain that foo.bar += baz ( += on a dotted name) implies a plain assignment (setattr) on foo.bar while foo_bar += baz ( += on bare name) might imply a := assignment (rebinding a nonlocal) IF there are no "foo_bar = baz" elsewhere in the same function BUT it would imply a plain assignment if there ARE other plain assignments to the same name in the same function, ...
I think you're making this sound more complicated than it is. I don't think you'll ever *have* to explain this anyway, as long as := and += use the same rules to find their target (I'd even accept rejecting the case where the target is a global for which the compiler can't find a single assignment, breaking an utterly minuscule amount of bad code, if any). I'm *not* saying that I like := (so far I still like 'global x in f' better) but I think that either way of allowing rebinding nonlocals will also have to allow rebinding them through += and friends. I think the main weakness (for me) of := and other approaches that try to force you to say you're rebinding a nonlocal each time you do it is beginning to show: there are already well-established rules for deciding whether a bare name is local or not, and those rules have always worked "at a distance". The main reason for disallowing rebinding nonlocals in the past has been that one of those rules was "if there's a bare-name assignment to it it must be local (unless there's also a global statement for it)" (and I couldn't find a satisfactory way to add a nonlocal declarative statement and I didn't think it was a huge miss -- actually I still think it's not a *huge* miss).
IOW it seems to me that we're getting into substantial amounts of subtlety in explaining (and thus maybe in implementing) a functionality change that's not terribly useful anyway and may damage rather than improve readability when it's used.
Taking the typical P. Graham accumulator example, say:
with += rebinding, we can code this:
def accumulator(n=0): def increment(i): n += i return n return increment
but without it, we would code:
def accumulator(n=0): def increment(i): n := n + i return n return increment
and it doesn't seem to me that the two extra keystrokes are to be considered a substantial price to pay.
That's the argument that has always been used against += by people who don't like it. The counterargument is that (a) the savings in typing isn't always that small, and (b) += *expresses the programmer's thought better*. Personally I expect that as soon as nonlocal rebinding is supported in any way, people would be hugely surprised if += and friends were not.
Admittedly in such a tiny example readability is just as good either way, as it's obvious which n we're talking about (there being just one, and extremely nearby wrt the point of use of either += or := ).
Suppose we wanted to have the accumulator "saturate" -- if the last value it returned was > m it must restart accumulating from zero. Now, without augmented assignment:
def accumulator_saturating(n=0, m=100): def increment(i): if n > m: n := i else: n := n + i return n return increment
we have a pleasing symmetry and no risk of errors -- if we mistakenly use an = instead of := in either branch the compiler will be able to let us know immediately. (Actually I'd be quite tempted to code the if branch as "n := 0 + i" to underscore the symmetry, but maybe I'm just weird:-).
If we do rely on augmented assignment being "rebinding":
def accumulator_saturating(n=0, m=100): def increment(i): if n > m: n = i else: n += i return n return increment
the error becomes a runtime rather than compile-time one, and does take a (small but non-zero) time to discover it.
Hah. Another argument *against* rebinding by :=, and *for* a nonlocal declaration. With 'nonlocal n, m' in increment() (or however it's spelled :-) the intent is clear.
The += 's subtle new semantics (rebinds either a local or nonlocal, depending on how other assignments elsewhere in the function are coded) do make it slightly harder to understand and explain, compared to my favourite approach, which is: := is the ONLY way to rebind a nonlocal name (and only ever does that, only with a bare name on LHS, etc, etc) which can't be beaten in terms of how simple it is to understand and explain. The compiler could then diagnose an error when it sees := and += used on the same barename in the same function (and perhaps give a clear error message suggesting non-augmented := usage in lieu of the augmented assignment).
Can somebody please show a compelling use case for some "nonlocal += expr" over "nonlocal := nonlocal + expr" , sufficient to override all the "simplicity" arguments above? I guess there must be some, since popular feeling appears to be in favour of having augmented-assignment as "rebinding", but I can't see them.
--Guido van Rossum (home page: http://www.python.org/~guido/)