Python列表复制:old[:] 和 list(old) 有什么区别?

7
old = [1, 2, 3]

以下两行代码有什么区别(如果有的话)?
new = old[:]
new = list(old)

更新:我已经接受了ubershmekel的答案,但后来我了解到一个有趣的事实:[:]适用于小型列表(10个元素),而list()对于更大的列表(100000个元素)来说速度更快。

~$ python -S -mtimeit -s "a = list(range(10))" "a[:]"
1000000 loops, best of 3: 0.198 usec per loop
~$ python -S -mtimeit -s "a = list(range(10))" "list(a)"
1000000 loops, best of 3: 0.453 usec per loop
~$ python -S -mtimeit -s "a = list(range(100000))" "a[:]"
1000 loops, best of 3: 675 usec per loop
~$ python -S -mtimeit -s "a = list(range(100000))" "list(a)"
1000 loops, best of 3: 664 usec per loop
6个回答

6
如果old不是列表,那么old[:]将会是与old相同容器类型的所有元素(可能是元组或字符串),而list(old)将会是一个包含相同元素的列表。
例如,如果old是字符串'foo',则old[:]将会是字符串'foo',而list(old)将会是列表['f', 'o', 'o']。

谢谢,这很相关,但我特别询问的是 old 是列表的情况(请看我的第一行, old = [1, 2, 3])。 - bpgergo
@bpgergo 好的,我只是把那行代码作为旧代码的例子。无论如何,希望没有造成任何伤害。 :-) - Rasmus Kaj

4
是的,有一点小区别。最近在/r/python上讨论过这个问题,在这篇博客文章中说明了一个可读性的不同之处,即[:]不能与生成器一起使用,并且保持与原始类型相同。
从技术上讲,你得到的是同样的东西——一个全新的列表,仍然指向相同的对象。选择你喜欢的方法(尽管[:]稍微快一些)。个人认为博客文章中的list(old)更易读。
回答关于具体差别的评论,在python 3.2中:
>>> import dis
>>> def colon(x):
...     return x[:]
...
>>> def new_list(x):
...     return list(x)
...
>>>
>>> dis.dis(colon)
  2           0 LOAD_FAST                0 (x)
              3 LOAD_CONST               0 (None)
              6 LOAD_CONST               0 (None)
              9 BUILD_SLICE              2
             12 BINARY_SUBSCR
             13 RETURN_VALUE
>>> dis.dis(new_list)
  2           0 LOAD_GLOBAL              0 (list)
              3 LOAD_FAST                0 (x)
              6 CALL_FUNCTION            1
              9 RETURN_VALUE
>>>

我认为list(old)较慢的原因是切片机制不需要在列表构造函数上执行“LOAD_GLOBAL”和“CALL_FUNCTION”,整个操作都在C中处理。

@MattFenwick list(x)需要查找全局变量list并调用一个带有一个参数的函数。 x[:]两者都不做,因此它的开销较小。如果是一个长列表,差异将是微不足道的(即使对于短列表也可能是无关紧要的)。 - Duncan
1
可读性有时是主观的,取决于背景。在那篇博客或其他相关文章中,我读到他们称a[:]符号为“神秘的”。嗯... - joaquin
当你想要清空一个列表而不重新绑定名称时,[:]是最有用的,即 old[:] = [] - Ethan Furman
因为在某些情况下,“old”可能不是唯一绑定到该列表对象的名称,而且您希望其他名称也能看到更新。 - Ethan Furman
我读了同样的博客文章,它很有趣,但没有解释其中的区别,因此让我感到不满。感谢Reddit链接和解释。 - bpgergo

1
在您提供的示例中,没有区别:两者都会得到一个拥有相同元素的全新list对象。
一般来说:
  • new = old[:] 仅适用于支持切片的对象,而 new 将与old相同类型
  • new = list(old)可与任何可迭代对象一起使用, new 将是一个list

0

还没有人提到这两者之间的功能差异;list(a_generator)可以工作,而a_generator[:]毫无意义:

>>> i = iter([1,2,3])
>>> i[:]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'listiterator' object is not subscriptable
>>> list(i)
[1, 2, 3]

谢谢,这很相关,但我特别问的是当old是一个列表时的情况(请看我的第一行,old = [1, 2, 3])。 - bpgergo

0
如果old是一个数组,那么两种语法是等效的。但是如果old是某种迭代器,只有list()语法才能工作。因此,第二种语法可能更可取,因为它更通用。

0

new = old[:] 使用切片符号创建列表old的副本,并将其存储在new中。

new = list(old)old强制转换为类型list。它已经是一个列表并分配给new

我相信这两者都会产生相同的结果。也就是说,new包含了old的副本。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接