|
| >On Fri, 22 Feb 2008 11:00:17 -0800, Aahz wrote:
| >
| >> It's just too convenient to be able to write
| >>
| >> L += ['foo']
| >>
| >> without rebinding L.
| >
| ><nitpick>But ``+=`` does rebind.</nitpick>
|
| Usually, but there's an exception for lists, which a specific
| implementation for += that calls "append". Or do I misunderstand you?
There is no exception at the compiler level, and indeed, cannot be, because
in general, the compiler *does not know* the types of either target or
augment.
target += augment
is compiled, in effect, as
compute target and load target_object
compute and load augment_object
call target_object.__iadd__(augment_object)
store return_object at target
In the list case, the rebinding is to the *same* object! --
because list.__iadd(l1,l2)__ returns l1 extended with l2 rather than a new
list l1+l2, as the specification allows.
Augmented (inplace) assignment is still assignment (binding).
| C:\tmp>python
| Python 2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)] on
| win32
| Type "help", "copyright", "credits" or "license" for more information.
| >>> L = [1,2,3]
| >>> id(L)
| 10351000
| >>> L += [4]
| >>> id(L)
| 10351000
L has been rebound to the same object, hence same id. Unfortunately, such
rebinding is only visible (that I can think of) when it fails:
Traceback (most recent call last):
File "<pyshell#8>", line 1, in -toplevel-
t[0]+=[1]
TypeError: object does not support item assignment([1],)
The list comprising t[0] is extended by its __iadd__ method. The exception
arises due to the subsequent attempted rebinding to t[0] (see dis output
below), which is, of course, not permissible.
([1, 2],)
Extension without attempted illegal rebinding raises no error.
CPython specifics:
1 0 LOAD_FAST 0 (i)
3 LOAD_CONST 1 (1)
6 INPLACE_ADD
7 STORE_FAST 0 (i)
10 LOAD_FAST 0 (i)
13 RETURN_VALUE
def fl(l): l += [1]; return l
dis(fl)
1 0 LOAD_FAST 0 (l)
3 LOAD_CONST 1 (1)
6 BUILD_LIST 1
9 INPLACE_ADD
10 STORE_FAST 0 (l)
13 LOAD_FAST 0 (l)
16 RETURN_VALUE
Treating the VM stack as a Python list, INPLACE_ADD executes
something like
stack[-2] = stack[-2].__iadd__(stack[-1])
stack.pop() # leaving returned object on top of stack
Terry Jan Reedy