[Python-Dev] replacing 'global'

Alex Martelli aleaxit at yahoo.com
Mon Oct 27 04:33:56 EST 2003


On Monday 27 October 2003 04:58 am, Guido van Rossum wrote:
> [attribution lost]
>
> > > > Ideally, augmented assignments would also become "rebinding".
> > > > However, this may have compatibility problems.
>
> [Alex]
>
> > > Unfortunately yes.  It might have been better to define them that way
> > > in the first place, but changing them now is dubious.
>
> [Greg]
>
> > I'm not so sure. You need an existing binding before an
> > augmented assignment will work, so I don't think there can
> > be any correct existing usages that would be broken by this.
>
> Indeed.  If x is neither local not declared global, x+=... is always
> an error, even if an x at an intermediate level exists, so THAT
> shouldn't be used as an argument against this.

Actually, if the compiler were able to diagnose that, it would be
wonderful -- but I don't think it can, because it can make no
assumptions regarding what might be defined in global scope (or
at least it definitely can't make any such assumptions now).

So, yes, any sensible program that works today would keep
working.  I dunno about NON-sensible programs such as:

def outer():
    x = 23
    def inner():
        exec 'x = 45'
        x+=1
    # etc etc

but then I guess the presence of 'exec' might be defined to
change semantics of += and/or disallow := or whatever else,
just as today it turns off local-variable optimizations.

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, ...

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.  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.  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.


Alex



Alex




More information about the Python-Dev mailing list