Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Why still list elements are not disappeared after using procedure?

Tags:

python

list

I define this function to do: [1,2,3] --> [2,3,1]

def shift_to_left(p):
    p.append(p[0])
    return p[1:]       

When I check like this, results are ok:

p1 = [1,2,3]
print p1
p1 = shift_to_left(p1)
print p1

The result:
[1, 2, 3]
[2, 3, 1]

However, when I introduce another list and concatenate as I go the result is different:

ss = []
p1 = [1,2,3]
ss.append(p1)
p1 = shift_to_left(p1)
ss.append(p1)

print ss

The result
[[1, 2, 3, 1], [2, 3, 1]]

But I want: 
[1,2,3]
[2,3,1]

why is it happening?

Thanks very much,

like image 813
Elyor Avatar asked Dec 29 '25 05:12

Elyor


1 Answers

In Python, most arguments are taken by reference.

Your function, shift_to_left, actually mutates its argument (through the use of append), but then returns a slice (which is a shallow copy of the list).

When you replace your original variable with the output of shift_to_left, this behaviour is hidden:

In [1]: def shift_to_left(p):
   ...:     p.append(p[0])
   ...:     return p[1:]
   ...: 

In [2]: xs = [1, 2, 3]

In [3]: xs = shift_to_left(xs)

In [4]: xs
Out[4]: [2, 3, 1]

But if we instead assign the result into a new variable, we can see that the original list has indeed been changed:

In [5]: ys = shift_to_left(xs)

In [6]: ys
Out[6]: [3, 1, 2]

In [7]: xs
Out[7]: [2, 3, 1, 2]

Our result, ys, is the slice of xs from the second element onwards. That's what you expected.

But xs itself has also been changed by the call to append: it's now one element longer than before. This is what you're experiencing in your second example.


If you do not want this behaviour, one way of avoiding it is by passing a copy of your list to shift_to_left:

In [8]: zs = shift_to_left(ys[:])

In [9]: zs
Out[9]: [1, 2, 3]

In [10]: ys
Out[10]: [3, 1, 2]

Here, you can see that the original list ys has not been modified, as shift_to_left was given a copy of it, not the object itself. (This is still passing by reference, of course; it's just not a reference to ys).


Alternatively, and probably more reasonably, you could change shift_to_left itself, so that it does not modify its arguments:

def shift_to_left(xs):
    return xs[1:] + xs[0]  # build a new list in the return statement

The big problem with both these approaches is that they create lots of copies of lists, which can be incredibly slow (and use a lot of memory) when the lists are large.


Of course, as @Marcin points out, if this is more than an academic exercise, you should probably use one of the built-in data structures such as deque.

like image 77
sapi Avatar answered Dec 31 '25 17:12

sapi