[Python-ideas] Globalize lonely augmented assignment

Bruce Frederiksen dangyogi at gmail.com
Sat Jun 12 21:40:16 CEST 2010


On Sat, Jun 12, 2010 at 2:28 PM, Georg Brandl <g.brandl at gmx.net> wrote:
> Am 12.06.2010 17:43, schrieb Bruce Frederiksen:
>
>> 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]
>
> (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.

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!

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

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

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

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



More information about the Python-ideas mailing list