0

Possible Duplicate:
Python passing list as argument

I looked for many topics about that, but I can't understand what happen really.

I have this code:

def altera(L1, L2):
    for elemento in L2:
        L1.append(elemento)
    L2 = L2 + [4]
    L1[-1] = 10
    del L2[0]
    return L2[:]

Lista1 = [1,2,3]
Lista2 = [1,2,3]

Lista3 = altera(Lista1, Lista2)

print Lista1
print Lista2
print Lista3

and the result is:

[1, 2, 3, 1, 2, 10]
[1, 2, 3]
[2, 3, 4]

I can't understand how the Lista1 was modified and Lista2 not. However before test the code, I thought that Lista1 and Lista2 would stay unmodified because they are global variables.

Community
  • 1
  • 1
Tomás Francisco
  • 791
  • 3
  • 7
  • 15

4 Answers4

3

Lists are passed by reference in Python. Lista1 is modified because you directly call .append:

for elemento in L2:
    L1.append(elemento)

Lista2 is not modified because you did not modify the list. You used the addition operator, L2 = L2 + [4], which does not modify L2. Instead, it creates a new list and returns the result.

If you Google the term "pass by reference" you should be able to find some good explanatory examples in Python.

Michael Koval
  • 8,207
  • 5
  • 42
  • 53
  • "Lists are passed by reference" is misleading in two ways: (1) Lists aren't special, everything is passed the same way. (2) Nothing's passed by reference unless you use a weird nonstandard definition of "pass by reference". "Call by sharing/object" has been proposed as avoiding these issues. –  Jan 22 '13 at 19:26
  • You're absolutely right. I was careless in my use of the term "pass-by-reference." Unfortunately, I don't know of a canonical term for "a reference passed by value. It would be great if we standardized on "call by sharing" or "call by object," but they don't seem to be commonly known terms. – Michael Koval Jan 23 '13 at 00:32
0

When you do L1.append(elemento) you are calling a method that actually changes the list named by the variable L1. All the other commands setting the values of L1 and L2 are actually just creating new names for new variables.

This version doesn’t change anything:

def altera(L1, L2):
    for elemento in L2:
        # create a new list and assign name L1
        L1 = L1 + [elemento]
    # create a new list and assign name L2
    L2 = L2 + [4]
    return L2

Lista1 = [1,2,3]
Lista2 = [1,2,3]

Lista3 = altera(Lista1, Lista2)

print Lista1
print Lista2
print Lista3

While this one does:

def altera(L1, L2):
    for elemento in L2:
        # Call method on L1 that changes it
        L1.append(elemento)
    # Call method on L2 that changes it
    L2.append(4)
    # Change object pointed to by name L1 -- Lista1
    L1[-1] = 10
    # Change object pointed to by name L2 -- Lista2
    del L2[0]
    return L2[:]

Lista1 = [1,2,3]
Lista2 = [1,2,3]

Lista3 = altera(Lista1, Lista2)

print Lista1
print Lista2
print Lista3

However there is a tricky matter with L += [2] which is not exactly the same as L = L + 2. The Python Language Reference section on Augmented assignment statements explains the difference:

An augmented assignment expression like x += 1 can be rewritten as x = x + 1 to achieve a similar, but not exactly equal effect. In the augmented version, x is only evaluated once. Also, when possible, the actual operation is performed in-place, meaning that rather than creating a new object and assigning that to the target, the old object is modified instead.”

andrewdotn
  • 32,721
  • 10
  • 101
  • 130
  • But... when I put the line 4th as 'L2 += [4]', L2 is modified, why? – Tomás Francisco Jan 22 '13 at 19:18
  • @PTF In short, because `+=` is weird. It's not a shorthand for `L2 = L2 + [4]`, it calls `__iadd__` (instead of `__add__`) which does in-place modification instead of creating a new object (but only in the case of lists and some other types that define `__iadd__`). –  Jan 22 '13 at 19:28
0

When you assigned a new value to the name L2 with the L2 = L2 + [4] statement, it no longer refers to Lista2. Most of your other statements change the value of the objects to which they already refer.

martineau
  • 119,623
  • 25
  • 170
  • 301
  • But... when I put the line 4th as 'L2 += [4]', L2 is modified, why? – Tomás Francisco Jan 22 '13 at 19:19
  • 1
    Because the `+=` operator changes the object on the left side of the assignment. It's called an "in-place" operator. `x += y` is equivalent to `x = operator.iadd(x, y)` and `list.iadd(x, y)` returns the modified list in this case. – martineau Jan 22 '13 at 19:27
  • See the docs on [Inplace Operators](http://docs.python.org/3/library/operator.html?highlight=inplace%20operators#inplace-operators). – martineau Jan 22 '13 at 19:34
0

There are two different things happening in your function:

def altera(L1, L2):
    for elemento in L2:
        L1.append(elemento)
        # [1] this ^ alters L1 in place
    L2 = L2 + [4]
    # [2] this ^ creates a new list
    L1[-1] = 10
    del L2[0]
    return L2[:]

To expand on the comments I added inline:

  1. inside altera, the variable L1 is whatever you passed in (so it is Lista1). It isn't a copy, a new list with the same contents - it refers to the same actual object
  2. when you assign L2 = L2 + [4] you do two things:

    1. create a new list with the value L2 + [4]
    2. set L2 to point to this new list, instead of whatever you passed in

    If we re-name the variable it becomes explicit, and works exactly the same:

    L3 = L2 + [4]
    del L3[0]
    return L3[:]
    

    Alternatively, if you want to modify the original L2 in place (ie, to actually modify Lista2), you would do

    L2.append(4)
    del L2[0]
    return L2[:]
    

    Note the last line still means Lista3 will be a different list object to Lista2, but it will have the same values. If you just return L2, the Lista3 will be identical with L2, so they'll be two names for the same list object.

If you want to prevent this behaviour, you can call altera like this:

Lista3 = altera(Lista1[:], Lista2[:])

now, inside altera, it will be working on its own copies of the arguments, and can't affect the original Lista1 and Lista2.

Useless
  • 64,155
  • 6
  • 88
  • 132