Python:为什么仍然列表元素在使用过程后不会消失?
我将此函数定义为执行:[1,2,3]-->[2,3,1]Python:为什么仍然列表元素在使用过程后不会消失?,python,list,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 但是,当我引入另一个列表并在进行连接时,结果是不同的: ss = [] p1 = [1,2,3] ss.append(p1) p1 = shift_to_left(p
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
但是,当我引入另一个列表并在进行连接时,结果是不同的:
ss = []
p1 = [1,2,3]
ss.append(p1)
p1 = shift_to_left(p1)
ss.append(p1)
print ss
为什么会这样 非常感谢,如果您运行代码,您会注意到
ss
仍然指向p1
的原始副本(由于p.append(p[0])
),其中当它被重新分配时,p1
一起指向已知列表,从而导致该行为。(第10步,共11步)
(p
发生突变,并且ss[0]=p
)
(p1
被分配给一个新的列表,后者被附加到ss
)
为什么会这样
returnp[1:][/code>是“非破坏性的”:它创建一个新列表。然而,p.append(p[0])
是“破坏性的”:它改变了p
本身
首先将p1
附加到ss
。这使得[[1,2,3]
,其中[1,2,3]
是p1
然后执行左移
,将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]]
如果您想移动/旋转列表中的元素,我认为最好使用a,而不是重新发明轮子。例如:
from collections import deque
d = deque([1,2,3])
d.rotate(-1)
print(d)
#[2, 3, 1]
在Python中,大多数参数都是通过引用获取的
您的函数,shift_to_left
,实际上改变了它的参数(通过使用append),但随后返回一个切片(它是列表的浅层副本)
当您将原始变量替换为shift\u to\u 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]
但如果我们将结果赋给一个新变量,我们可以看到原来的列表确实发生了变化:
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\u to\u 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\u to\u left
得到了它的副本,而不是对象本身。(当然,这仍然是通过引用传递的;它只是没有引用到ys
)
或者,可能更合理地说,您可以将shift\u更改为\u left
本身,这样它就不会修改其参数:
def shift_to_left(xs):
return xs[1:] + xs[0] # build a new list in the return statement
这两种方法的最大问题是它们都会创建大量列表副本,当列表很大时,这些副本的速度会非常慢(并且会占用大量内存)
当然,正如@Marcin所指出的,如果这不仅仅是一个学术练习,那么您可能应该使用一种内置的数据结构,例如deque
尝试以下方法:
p1 = [1,2,3]
p1 = shift_to_left(p1)
ss = []
ss.extend(p1)
print ss
打印[2,3,1]
。改用extend()
,因为append()
将在数组中创建一个数组。您还额外调用了ss.append(p1)
返回p[1:]
不会修改原始列表。其他人已经以多种方式解释了发生的情况,因此我将只提及函数的最简单正确实现:返回p[1:::+[p[0]
检查id:printid(p1)
通话前后,以及ss
内容的ID。非常感谢各位,这是我第一次发布问题。答案和动态社区给我留下了深刻的印象。解决了问题,但没有回答问题(“为什么”):)Meh,+1。尽管我同意,但问题本身与“为什么”有关。像这样的问题在大学考试中很常用。因此,深入理解为什么与备选方案相反是非常重要的,尤其是在这种情况下。@Amadan你说得对,我认为其他Anwser,很好地回答了OP问题。但是使用deque可能更快、更容易、更不容易出错,并且通常是OP想要实现的更推荐的方法。他想要数组中的一个数组,理想的解决方案(格式很差,但仍然声明)是[[1,2,3],[2,3,1]]
。这是一个很好的教育工具。@Amadan:这个东西救了我很多次。尤其是指针相关的东西。我非常感谢这个人。“在Python中,大多数参数都是通过引用获取的”是不正确的。在Python中,所有参数都是通过赋值传递的(如果愿意,也可以通过对象传递)。有关解释,请参阅。@KirkStrauser除非我误解了这篇文章,它是说通过赋值传递是通过对可变类型(即,对于大多数类型)的引用。要记住的最大一点是,传递的是变量指向的对象,而不是变量本身。这是一个微妙但重要的区别。
def shift_to_left(xs):
return xs[1:] + xs[0] # build a new list in the return statement
p1 = [1,2,3]
p1 = shift_to_left(p1)
ss = []
ss.extend(p1)
print ss