[Python-bugs-list] [ python-Feature Requests-753600 ] why should += produce name binding?

SourceForge.net noreply@sourceforge.net
Mon, 16 Jun 2003 14:37:34 -0700


Feature Requests item #753600, was opened at 2003-06-12 18:16
Message generated for change (Comment added) made by gregsmith
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=355470&aid=753600&group_id=5470

Category: Parser/Compiler
Group: None
Status: Closed
Resolution: Rejected
Priority: 5
Submitted By: Gregory Smith (gregsmith)
Assigned to: Nobody/Anonymous (nobody)
Summary: why should += produce name binding?

Initial Comment:

Currently, in

def x():
     foo += 1

... 'foo' is a local variable. Why should it be?
If the semantics are changed so that augmented
assignment is not a name-binding operation,
then only broken code will be affected.

This would allow you to use simple things
like   'EventCount += 1' without having to
use 'global EventCount'. After all, I can do
'EventList.append(...)' without the global decl.

For another (better) example, see 
http://mail.python.org/pipermail/edu-sig/2001-June/001329.html

In the response to the above, the poster is referred
to PEP227 which lists 'assignment' as a name-binding
operation. There is no clear-cut implication that this
includes augmented assignment, and in the Python
ref manual, one can only infer this behaviour from
the statement that x += y is almost equivalent
to x = x+y, which is pretty weak. In any case, since
an augmented assignment to a name always requires the
a-priori existence of that name in
an accessible namespace,  IMHO it should not
produce a binding.


















----------------------------------------------------------------------

>Comment By: Gregory Smith (gregsmith)
Date: 2003-06-16 17:37

Message:
Logged In: YES 
user_id=292741

I don't follow the first part. name binding does not
happen at run time, but at compile time.
 If there is no binding, the name would be assumed
global by the compiler. So you would get this for
the first two x() examples:

          9 LOAD_GLOBAL              0 (cnt)
         12 LOAD_CONST               1 (1)
         15 INPLACE_ADD
         16 STORE_GLOBAL             0 (cnt)


which is currently what you get if the 'global' is present,
and would work if the global var is present when run.

I was never suggesting that there should be an implicit
'global' on each +=, rather, that in the absence of
 *other* name binding operations on the same
name, += would fail to create a local -- which is
another way of saying that "+= is not a name
binding operation", which is the way
I phrased the request in the first place.

Basically, a+=1
would fall into the same category, in terms of name
scopes, as a.b +=1. There is no implicit 'global a'
when you write a.b +=1, or a(), but if you don't mention
'a' anywhere else, that's what you get.


I'm still confused as to why this is so radical - basically,
the rationale is that you can't += on a name if it
isn't defined, which, currently, it can't possibly be
if there are no other name binding ops in the block.

It seems unwarranted to create a local (at compile
time) due to the existence of an assignment op that requires
(at run time) that local to be previously
defined, and in the absence (at compile time)
of any statement that would cause it to be defined
at run time. But that is the current behaviour.

The problem with the RFE only occurs, in my view,
when you += on a local belonging to an enclosing
scope, since the consistent application of the RFE
would produce an operation (rebinding of an
enclosing scope's local variable) which may be harmful 
somehow, and for which an opcode may not even exist, since
this is currently impossible. This is what caused me to
retract it -
I hadn't seen that implication in the first place, and
don't know how to deal  with it.



----------------------------------------------------------------------

Comment By: Martin v. Löwis (loewis)
Date: 2003-06-16 16:28

Message:
Logged In: YES 
user_id=21627

Just *not* making += a name-binding operation has a
different effect than the one you expect. There would *not*
be an implicit global declaration. Instead, lookup would
first find the global. It then would invoke __iadd__, which
is not supported for integers. So it would fall-back to
__add__, which produces a new value. Since += is not
name-binding, no name would be bound by that operation, so
the result is simply discarded. IOW, your example would be
equivalent to

def x():
  cnt + 1

which, I assume, is not what you want.

OTOH, I can also interpret your last message as saying "+="
should imply a global statement. In that case, the function

def y():
  cnt = 1
  cnt += 1

would change to

def y():
  global cnt
  cnt = 1
  cnt += 1

which I think is also not what you want. So what is it that
you want?

I interpret your last sentence that you withdraw this RFE;
so I'm closing it.

----------------------------------------------------------------------

Comment By: Gregory Smith (gregsmith)
Date: 2003-06-16 14:59

Message:
Logged In: YES 
user_id=292741

I was not proposing any change to 
the semantics of the operation itself.
Currently, all symbols in a function
definition are locals by default if
they appear as the target of a name
binding operation: "argument
declaration, assignment, class and
function definition, import statements,
for statements and except clauses"
according to PEP227.

I was proposing that augmented assignments
be removed from this list. So that,

def x():
	cnt += 1

(currently unusable) would be equivalent
to 

def x():
	global cnt
	cnt += 1


whereas this would remain unusable:

def x():
	cnt = cnt + 1

Clearly, this change is independent of
whether or not x is mutable since the
parser can't tell. 

Taking another look at Kirby Urner's June/01
message (see link in original msg), I see
a subtley which maybe is what loewis
was alluding to. Urner 'fixes' the problem
there (which relates to nested scopes) by copying
an outer local variable in to a local and
doing += on it (e.g. replacing a+=1 by
s=a; s+=1); this may or may not change 
'a' in the outer scope depending 
on whether it is mutable, whereas the proposed
functionality of a+=1 would change it. The
's=a; s+=1' will never rebind 'a', whereas
the proposed 'a+=1' may do so (but probably
only when it is immutable, when the
change of id is an inevitable side effect
anyway).


So, looking at it again, the example
either adds some more ammo
to my point, or kills it. Suppose you
really want to change 'a': you can't fix
this by using 'global a', since that
would select the top-level scope, not the
containing scope.

Question is, should a nested function
be allowed to do += on an outer
function's local variables, potentially
rebinding them? maybe not. In any case,
as long as there is no way to do direct
assignment on those variables, it would
be a little strange to be allowed +=.

This has tipped the scales for me.
PEP  retracted, on the basis of
my possibly flawed understanding of
this point ...




----------------------------------------------------------------------

Comment By: Raymond Hettinger (rhettinger)
Date: 2003-06-16 10:20

Message:
Logged In: YES 
user_id=80475

In your example, I prefer the global declaration for 
EventCount.  It adds clarity and allows the reader to 
understand the function without reading the enclosing 
scopes.

I do agree the the += semantics are thorny area for python, 
I prefer the current binding behavior which is what I expect 
out of an assignment operation.

----------------------------------------------------------------------

Comment By: Martin v. Löwis (loewis)
Date: 2003-06-14 12:24

Message:
Logged In: YES 
user_id=21627

Can you please explain, in detail, what semantics you
propose for += if the variable does not refer to a mutable
object?

----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=355470&aid=753600&group_id=5470