[Python-Dev] replacing 'global'

Guido van Rossum guido at python.org
Mon Oct 27 10:11:16 EST 2003


> 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/)



More information about the Python-Dev mailing list