python按值列出,而不是按引用列出

python list by value not by reference

本问题已经有最佳答案,请猛点这里访问。

让我们举个例子

1
2
3
4
5
6
7
a=['help', 'copyright', 'credits', 'license']
b=a
b.append('XYZ')
b
['help', 'copyright', 'credits', 'license', 'XYZ']
a
['help', 'copyright', 'credits', 'license', 'XYZ']

我想在列表"b"中附加值,但列表"a"的值也发生了变化。< BR>我想我不知道为什么它是这样的(Python通过引用传递列表)。< BR>我的问题是"我如何通过值传递它,以便附加"b"不会更改"a"中的值?"


正如官方python常见问题解答中所回答的:

1
b = a[:]


要复制列表,您可以使用list(a)a[:]。在这两种情况下,都会创建一个新对象。然而,这两种方法对可变对象集合有限制,因为内部对象保持其引用的完整性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> a = [[1,2],[3],[4]]

>>> b = a[:]
>>> c = list(a)

>>> c[0].append(9)

>>> a
[[1, 2, 9], [3], [4]]
>>> c
[[1, 2, 9], [3], [4]]
>>> b
[[1, 2, 9], [3], [4]]
>>>

如果需要对象的完整副本,则需要copy.deepcopy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> from copy import deepcopy
>>> a = [[1,2],[3],[4]]

>>> b = a[:]
>>> c = deepcopy(a)

>>> c[0].append(9)

>>> a
[[1, 2], [3], [4]]
>>> b
[[1, 2], [3], [4]]
>>> c
[[1, 2, 9], [3], [4]]
>>>


就性能而言,我最喜欢的答案是:

1
b.extend(a)

检查相关备选方案在性能方面如何相互比较:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In [1]: import timeit

In [2]: timeit.timeit('b.extend(a)', setup='b=[];a=range(0,10)', number=100000000)
Out[2]: 9.623248100280762

In [3]: timeit.timeit('b = a[:]', setup='b=[];a=range(0,10)', number=100000000)
Out[3]: 10.84756088256836

In [4]: timeit.timeit('b = list(a)', setup='b=[];a=range(0,10)', number=100000000)
Out[4]: 21.46313500404358

In [5]: timeit.timeit('b = [elem for elem in a]', setup='b=[];a=range(0,10)', number=100000000)
Out[5]: 66.99795293807983

In [6]: timeit.timeit('for elem in a: b.append(elem)', setup='b=[];a=range(0,10)', number=100000000)
Out[6]: 67.9775960445404

In [7]: timeit.timeit('b = deepcopy(a)', setup='from copy import deepcopy; b=[];a=range(0,10)', number=100000000)
Out[7]: 1216.1108016967773


此外,您还可以执行以下操作:

1
b = list(a)

这将适用于任何序列,甚至那些不支持索引器和切片的序列…


如果要复制一维列表,请使用

1
b = a[:]

但是,如果a是一个二维列表,那么这对您来说就不太管用了。也就是说,a中的任何变化也将反映在b中。在这种情况下,使用

1
b = [[a[x][y] for y in range(len(a[0]))] for x in range(len(a))]

正如他在回答中提到的,

1
b = a[:]

这对您的情况很有用,因为切片列表会创建列表的新内存ID(意味着您不再引用内存中的同一对象,对其中一个对象所做的更改不会反映在另一个对象中。)

但是,有一个小问题。如果您的列表是多维的,就像列表中的列表一样,简单的切片不会解决这个问题。在更高维度中所做的更改,即原始列表中的列表,将在两者之间共享。

别担心,有解决办法。模块复制有一种巧妙的复制技术来解决这个问题。

1
2
3
from copy import deepcopy

b = deepcopy(a)

将复制具有新内存ID的列表,无论它包含多少级别的列表!


当您执行b = a操作时,只需创建指向同一内存的另一个指针,这就是为什么当你附加到b时,a也会改变。

您需要创建的副本,操作如下:

1
b = a[:]

要创建列表的副本,请执行以下操作:

1
b = a[:]

我建议采用以下解决方案:

1
2
b = []
b[:] = a

这将把所有元素从A复制到B。副本将是值副本,而不是引用副本。


我发现我们可以使用extend()来实现copy()的函数

1
2
3
4
a=['help', 'copyright', 'credits', 'license']
b = []
b.extend(a)
b.append("XYZ")

b = list(a)

请参阅http://henry.precheur.org/python/copy_list。