在 "cloning" 对象时避免使用 clone = obj *1 是否有特殊原因?

Is there a particular reason that using clone = obj *1 is avoided when "cloning" objects?

this SO thread 中表明,就复制项目而言,切片比任何方法都快。

使用:

list1 = ['foo','bar']
copy1 = list1 * 1
list1.pop()
print 'list1: ' + list1
print 'copy1: ' + copy1

我得到:

list1: ['foo']
copy1: ['foo', 'bar']

有什么特别理由要避免制作这样的副本吗?

不要忘记您正在复制对 相同 对象的引用,这不是您(通常)期望的:

如果您要创建一个包含 3 个“1”列表的 list

>>> lst = [[1]] * 3
>>> lst
[[1], [1], [1]]

现在对第一项做点什么:

>>> lst[0].append(2)

糟糕,这三个位置都是同一个对象:

>>> lst
[[1, 2], [1, 2], [1, 2]]

我希望 [[1, 2], [1], [1]],但我想这是主观的,这就是我不会使用它的原因。至少不是可变对象。

就复制项目而言,切片比任何方法都快。

您可以自己轻松测试:

timeit.timeit('clone = lst * 1', 'lst = ["foo", "bar"]', number=10000000)

对我来说,我的时间是0.8731180049981049

用切片代替相同的测试:

timeit.timeit('clone = lst [:]', 'lst = ["foo", "bar"]', number=10000000)

给出时间:0.9454964299984567

所以 * 运算符似乎实际上更快。至于为什么人们使用 [:] 代替?它的作用更加明显。


这在复制列表的列表时出现故障,请考虑:

>>> lst = [[1], [2]]

使用切片和 *1 复制仍然允许修改原始列表

>>> copy1 = lst[:]
>>> copy1[0].append('x')
>>> lst # is affected by the above
[[1, 'x'], [2]]

>>> copy2 = lst * 1
>>> copy2[0].append('y')
>>> lst # is also affected by the above
[[1, 'x', 'y'], [2]]

但是通过列表推导可以深度复制序列

>>> copy3 = [l[:] for l in lst]
>>> copy3[0].append('z')
>>> lst # unaffected
[[1, 'x', 'y'], [2]]

>>> copy4 = [l * 1 for l in lst]
>>> copy4[0].append('z')
>>> lst # also unaffected
[[1, 'x', 'y'], [2]]