python列表的值不是通过引用

d.p*_*tto 139 python reference list

我们来举个例子吧

a=['help', 'copyright', 'credits', 'license']
b=a
b.append('XYZ')
b
['help', 'copyright', 'credits', 'license', 'XYZ']
a
['help', 'copyright', 'credits', 'license', 'XYZ']
Run Code Online (Sandbox Code Playgroud)

我想在列表'b'中附加值,但列表'a'的值也已更改.
我想我不知道为什么会这样(python通过引用传递列表).
我的问题是"如何通过值传递它,以便附加'b'不会改变'a'中的值?"

phi*_*hag 190

正如官方Python FAQ中所回答:

b = a[:]
Run Code Online (Sandbox Code Playgroud)

  • 对我不起作用.我对`b`所做的任何改变也可以在`a`中看到. (9认同)
  • 但是,如果 a 是二维列表,这将不起作用 (6认同)

joa*_*uin 125

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

>>> 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]]
>>> 
Run Code Online (Sandbox Code Playgroud)

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

>>> 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]]
>>> 
Run Code Online (Sandbox Code Playgroud)


Kyr*_*Kyr 26

在性能方面,我最喜欢的答案是:

b.extend(a)
Run Code Online (Sandbox Code Playgroud)

检查相关替代方案在性能方面的相互比较:

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
Run Code Online (Sandbox Code Playgroud)

  • 调用`extend()`的测试用例不能与其他用例相提并论。要使用`extend()`,您必须先创建一个数组,而其他结构将为您创建一个数组。因此,通过跳过列表对象的初始化,可以有效地为`extend()`提供一个优势。要更正测试,将“ b = []”从设置移动到要测试的语句,例如“ b = []”;b.extend(a)`。这将改变结果,有利于使用切片创建副本的第二种情况。 (2认同)

Dar*_*mas 16

此外,你可以这样做:

b = list(a)
Run Code Online (Sandbox Code Playgroud)

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


Roh*_*ena 9

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

b = a[:]
Run Code Online (Sandbox Code Playgroud)

但是,如果a是二维列表,这对您不起作用.也就是说,任何变化都a将反映在中b.在那种情况下,使用

b = [[a[x][y] for y in range(len(a[0]))] for x in range(len(a))]
Run Code Online (Sandbox Code Playgroud)


Jor*_*gni 8

正如phihag在他的回答中提到的,

b = a[:]
Run Code Online (Sandbox Code Playgroud)

因为切片列表会创建列表的新内存ID(这意味着您不再引用内存中的同一个对象,并且您对其中的更改将不会反映在另一个中,因此将适用于您的情况.)

但是,有一个小问题.如果您的列表是多维的,就像列表中的列表一样,只需切片就不能解决这个问题.在较高维度中进行的更改(即原始列表中的列表)将在两者之间共享.

不要担心,有一个解决方案.模块副本有一个漂亮的复制技术,可以解决这个问题.

from copy import deepcopy

b = deepcopy(a)
Run Code Online (Sandbox Code Playgroud)

无论它包含多少级别的列表,都会复制一个带有新内存ID的列表!


Rob*_*ers 5

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

b = a[:]
Run Code Online (Sandbox Code Playgroud)


Gil*_*l.I 5

当你这样做b = a,你只需创建另一个指针指向相同的内存一个,这就是为什么当你追加到b,一个变化太大.

你需要创建一个a的副本,并且这样做:

b = a[:]
Run Code Online (Sandbox Code Playgroud)