Python:为什么列表元素在使用程序后仍然没有消失?
Python: Why still list elements are not disappeared after using procedure?
我定义这个函数来做:[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]
为什么会这样?
非常感谢,
如果你 运行 你的代码 here,你会注意到 ss
仍然指向原始的(由于 p.append(p[0])
在你的 shift 函数中发生了变化)副本p1
,其中 p1
在重新分配时一起指向已知列表,从而导致行为。 (11 步中的第 10 步)
(p
发生变异,ss[0] = p
)
(p1
被分配到一个新的列表,后来附加到 ss
)
why is it happening?
return p[1:]
是 "non-destructive":它创建一个新列表。然而,p.append(p[0])
是 "destructive":它改变了 p
本身。
首先,您将 p1
附加到 ss
。这使得 [[1, 2, 3]]
,其中 [1, 2, 3]
是 p1
.
然后执行 shift_to_left
,将 p1
更改为 [1, 2, 3, 1]
和 returns [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]]
如果你想 shift/rotate 列表中的元素,我认为更好的方法是使用 deque,而不是重新发明轮子。例如:
from collections import deque
d = deque([1,2,3])
d.rotate(-1)
print(d)
#[2, 3, 1]
在Python中,大部分论点都是引用。
您的函数 shift_to_left
实际上改变了它的参数(通过使用追加),但随后 returns 一个切片(它是列表的浅表副本)。
当您将原始变量替换为 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
.
试试这个:
p1 = [1,2,3]
p1 = shift_to_left(p1)
ss = []
ss.extend(p1)
print ss
打印 [2, 3, 1]
。使用 extend()
代替,因为 append()
将在数组中创建一个数组。您还额外调用了 ss.append(p1)
.
我定义这个函数来做:[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]
为什么会这样?
非常感谢,
如果你 运行 你的代码 here,你会注意到 ss
仍然指向原始的(由于 p.append(p[0])
在你的 shift 函数中发生了变化)副本p1
,其中 p1
在重新分配时一起指向已知列表,从而导致行为。 (11 步中的第 10 步)
(p
发生变异,ss[0] = p
)
(p1
被分配到一个新的列表,后来附加到 ss
)
why is it happening?
return p[1:]
是 "non-destructive":它创建一个新列表。然而,p.append(p[0])
是 "destructive":它改变了 p
本身。
首先,您将 p1
附加到 ss
。这使得 [[1, 2, 3]]
,其中 [1, 2, 3]
是 p1
.
然后执行 shift_to_left
,将 p1
更改为 [1, 2, 3, 1]
和 returns [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]]
如果你想 shift/rotate 列表中的元素,我认为更好的方法是使用 deque,而不是重新发明轮子。例如:
from collections import deque
d = deque([1,2,3])
d.rotate(-1)
print(d)
#[2, 3, 1]
在Python中,大部分论点都是引用。
您的函数 shift_to_left
实际上改变了它的参数(通过使用追加),但随后 returns 一个切片(它是列表的浅表副本)。
当您将原始变量替换为 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
.
试试这个:
p1 = [1,2,3]
p1 = shift_to_left(p1)
ss = []
ss.extend(p1)
print ss
打印 [2, 3, 1]
。使用 extend()
代替,因为 append()
将在数组中创建一个数组。您还额外调用了 ss.append(p1)
.