Python:为什么使用过程后列表元素仍然没有消失?

时间:2015-01-06 01:32:10

标签: python list

我将此函数定义为:[1,2,3] - > [2,3,1]

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

当我这样检查时,结果还可以:

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

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

但是,当我引入另一个列表并连接时,结果是不同的:

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]

为什么会这样?

非常感谢,

5 个答案:

答案 0 :(得分:4)

如果你想在列表中移动/旋转元素,我认为更好的方法是使用deque,而不是重新发明轮子。例如:

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

答案 1 :(得分:4)

在Python中,大多数参数都是通过引用获得的。

你的函数shift_to_left实际上改变了它的参数(通过使用append),但随后返回一个切片(这是列表的浅表副本)。

当您使用shift_to_left的输出替换原始变量时,此行为将被隐藏:

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]

但是如果我们将结果分配给 new 变量,我们可以看到原始列表确实已被更改:

In [5]: ys = shift_to_left(xs)

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

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

我们的结果ys是从第二个元素开始的xs切片。这就是你的期望。

xs本身的已被append调用更改:现在它比以前更长一个元素。 这就是你在第二个例子中遇到的情况。


如果你想要这种行为,一种避免这种行为的方法是将你的列表副本传递给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]

在这里,您可以看到原始列表ys尚未被修改,因为shift_to_left被赋予了它的副本,而不是对象本身。 (当然,这仍然是通过引用传递的;它不是对ys)的引用。


或者,可能更合理的是,您可以更改shift_to_left本身,以便它不会修改其参数:

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

这两种方法的最大问题在于它们会创建大量的列表副本,当列表很大时,列表的速度会非常慢(并且使用大量内存)。


当然,正如@Marcin指出的那样,如果这不仅仅是一个学术练习,你应该使用其中一个内置数据结构,例如deque

答案 2 :(得分:3)

如果您运行代码here,您会注意到ss仍然指向p.append(p[0])的{​​{1}}副本的原始版本(由于p1而在您的班次函数中发生变异),当p1重新分配时,p指向知道列表,从而导致行为。 (11中的第10步)

p is mutated

ss[0] = p变异,p1

p1 gets assigned to a new list altogether

ss完全分配到新列表,后者附加到{{1}})

答案 3 :(得分:1)

  

为什么会这样?

return p[1:]是“非破坏性的”:它会创建一个新列表。但是,p.append(p[0])具有“破坏性”:它会自行更改p

首先,您将p1追加到ss。这使[[1, 2, 3]]成为[1, 2, 3] p1

然后执行shift_to_left,将p1更改为[1, 2, 3, 1]并返回[2, 3, 1]。由于p1中包含ss,因此ss变为[[1, 2, 3, 1]],然后您将新的p1附加到[[1, 2, 3, 1], [2, 3, 1]]

更好的实施将完全是非破坏性的:

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

答案 4 :(得分:0)

试试这个:

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

  print ss

打印[2, 3, 1]。请改用extend(),因为append()将在数组中创建一个数组。你还额外打电话给ss.append(p1)