views:

155

answers:

4

When I execute (I'm using the interactive shell) these statements I get this:

L=[1,2,3]
K=L

L.append(4)

L
[1,2,3,4]
K
[1,2,3,4]

But when I do exactly the same thing replacing L.append(4) with L=L+[4] I get:

L
[1,2,3,4]
K
[1,2,3]

Is this some sort of reference thing? Why does this happen?

Another funny thing I noticed is that L+=[4] acts like .append, which is odd as I thought it would act like L = L + [4].

Clarification to all of this would be greatly appreciated.

Thanks

+2  A: 

With append you're modifying the list directly. With L=L+[4], you're making a copy of the original L and adding a new element, then assigning that result back to L and breaking its equivalence to K.

I'm not sure about the behavior of +=.

Mark Ransom
`+=` seems to behave like `append` (I just tested it)
Andre Holzner
@Andre, nope. It behaves like `extend`.
Aaron Gallagher
you're right `+= 5` didn't work..
Andre Holzner
+13  A: 
L.append(4)

This adds an element on to the end of the existing list L.

L += [4]

The += operator invokes the magic __iadd__() method. It turns out list overrides the __iadd__() method and makes it equivalent to extend() which, like append(), adds elements directly onto an existing list.

L = L + [4]

L + [4] generates a new list which is equal to L with 4 added to the end. This new list is then assigned back to L. Because you've created a new list object, K is unchanged by this assignment.

We can use id() to identify when a new object reference is created:

>>> L = [1, 2, 3]
>>> id(L)
152678284
>>> L.append(4)
>>> id(L)
152678284

>>> L = [1, 2, 3]
>>> id(L)
152680524
>>> L = L + [4]
>>> id(L)
152678316
John Kugelman
No. `__iadd__` on lists is equivalent to *`extend`*, not `append`.
Aaron Gallagher
@Aaron Yes, you're right. Fixed.
John Kugelman
A: 

In your first example K and L variable names refer to the same object, so when you invoke a method mutating that object, changes are obviously seen through both references. In the second example + operator invokes list.__add__ which returns new object (concatenation of two lists) and L name now refers to this new object, while K is intact.

rkhayrov
So, when I assign a variable to a list it gets the reference to the object? Is this because the actually list is not the list but rather a reference as well?I think I understand now. This could happen with a list and dictionary right? Because they are actually both references to the object themselves.Thank you so much for the clarification. The things I read elsewhere were just vaugue and didn't really say that using the + operator invoked the list.__add__Thanks (^_^)
aitee
Have any thoughts about the +=?
aitee
Not only `list` and `dict`, *everything* in Python is an object. All objects are stored in heap and all variables are just references. But some objects are immutable (like `int`s and `string`s), and some immutable objects are interned, so you don't get this puzzling behavior. You must distinguish between modifying the data and modifying the mapping between variable names and data. The latter is what happens when you assign something to a variable. Note, however, that variable mapping itself can be part of data if it represents members of an object.
rkhayrov
Re `+=` operator: this is just syntactic sugar for calling `__iadd__` method which modifies the object.
rkhayrov
Thanks, all of this is so helpful.
aitee
+1  A: 

If you are curious about the bytecodes:

>>> def L_app( ):
...     L.append( 4 )
...
>>> def L_add( ):
...     L = L + [ 4 ]
...
>>> def L_add_inplace( ):
...     L += [ 4 ]
...
>>> dis.dis( L_app )
  2           0 LOAD_GLOBAL              0 (L)
              3 LOAD_ATTR                1 (append)
              6 LOAD_CONST               1 (4)
              9 CALL_FUNCTION            1
             12 POP_TOP
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE
>>> dis.dis( L_add )
  2           0 LOAD_FAST                0 (L)
              3 LOAD_CONST               1 (4)
              6 BUILD_LIST               1
              9 BINARY_ADD
             10 STORE_FAST               0 (L)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE
>>> dis.dis( L_add_inplace )
  2           0 LOAD_FAST                0 (L)
              3 LOAD_CONST               1 (4)
              6 BUILD_LIST               1
              9 INPLACE_ADD
             10 STORE_FAST               0 (L)
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE
katrielalex
the most interesting case is the `+=` form.
aaronasterling
Though not new to programming, I'm quite new to python. This (the bytecodes) does not make a whole lot of sense to me. Aren't bytecodes generally codes which are lower level than the language, which are closer to machine code and quicker to run? Maybe I got the wrong definition.
aitee
@aaronasterling: interesting indeed! Added. @aitee: yes, they are. `dis` prints out a 'nice' representation of the bytecodes to which a Python function is compiled. This is often useful for seeing what is going on "under the hood".
katrielalex