Python: Why Still List Items Don't Disappear After Using Procedure?

I define this function: [1,2,3] → [2,3,1]

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

      

When I check like this, the 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 enter a different list and concatenation 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 this happening?

Many thanks,

+3


source to share


5 answers


In Python, most arguments are taken by reference.

Your function shift_to_left

actually mutates its argument (with append), but then returns a slice (which is an incomplete copy of the list).

When replacing the original variable with output, shift_to_left

this behavior 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 to a new variable, we can see that the original list has indeed been modified:

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 a slice xs

from the second element onwards. This is what you expected.

But itself has xs

also been changed by the call append

: this element is now longer than before. This is what you are experiencing in your second example.




If you don't want this behavior, one way to avoid it is to pass 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 was ys

not changed as shift_to_left

a copy of it was provided, not the object itself. (Of course, this is by reference, it just isn't a link to ys

).




Alternatively, and perhaps smarter, you can change shift_to_left

yourself so that it doesn't change 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 of these approaches is that they create many copies of the lists, which can be incredibly slow (and use up 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 like deque

.

+4


source


If you want to shift / rotate items in a list, I think it would be better to use a deque rather than reinvent the wheel. For example:



from collections import deque
d = deque([1,2,3])
d.rotate(-1)
print(d) 
#[2, 3, 1]

      

+4


source


If you run your code here , you may notice that it ss

remains to point to the original (mutated in your shift function due to p.append(p[0])

) copy p1

, where as p1

points to a familiar list together when it gets reassigned, resulting in behavior. (Step 10 of 11)

p is mutated

( p

becomes mutated as well ss[0] = p

)

p1 gets assigned to a new list altogether

( p1

completely assigned to the new list, which is added last to ss

)

+3


source


Why is this happening?

return p[1:]

is "non-destructive": it creates a new list. However, it p.append(p[0])

is "destructive": it changes itself p

.

First add p1

to ss

. It does [[1, 2, 3]]

where [1, 2, 3]

equal p1

.

Then you make your own shift_to_left

, which changes p1

to [1, 2, 3, 1]

and returns [2, 3, 1]

. Since it is p1

contained in ss

, your ss

becomes [[1, 2, 3, 1]]

, and then you add a new one p1

to the form [[1, 2, 3, 1], [2, 3, 1]]

.

A better implementation would be purely non-destructive:

def shift_to_left(p):
    return p[1:] + [p[0]]

      

+1


source


Try the following:

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

  print ss

      

This prints [2, 3, 1]

. Use instead extend()

because it append()

will create an array in an array. Plus, you had an extra challenge ss.append(p1)

.

0


source







All Articles