为什么python列表在使用=运算符时就是这样的

Why do python lists act like this when using the = operator

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

为什么会出现以下代码:

1
2
3
4
a = [1,2,3]
b = a
b[0] = 3
print(a)

B表修改后会打印吗?[3,2,3]。这也是为什么是真的,但是下面的代码:

1
2
3
4
a = [1,2,3]
b = a
b = [0,0,0]
print(a,b)

打印[1,2,3][0,0,0]??这似乎不一致。如果第一个代码为真,那么第二个代码不应该打印[0,0,0][0,0,0]?有人能解释一下吗?


在python中有两种类型的数据…可变的和不变的。数字、字符串、布尔值、元组和其他简单类型是不可变的。字典、列表、集合、对象、类和其他复杂类型是可变的。

当你说:

1
2
a = [1,2,3]
b = a

您在内存中创建了一个单一的可变列表,将a指定为指向它,然后将b指定为指向它。这在记忆中是一样的。

因此,当你改变它(修改它)时:

1
b[0] = 3

它是b指向同一存储位置的值的索引[0]的修改(突变)。

但是,当您更换它时:

1
b = [0,0,0]

它正在内存中创建一个新的可变列表,并指定b指向它。

查看id()功能。它会告诉你任何变量的"地址"。您可以看到哪些名称指向与id(varname)相同的内存位置。

奖励:python中的每个值都是通过引用传递的…也就是说,当您将它赋给一个变量时,它只会使该变量指向它在内存中的那个值。拥有不可变类型允许Python"重用"相同的内存位置来处理常见的不可变类型。

在解释器启动时考虑一些常见的值:

1
2
3
4
5
6
7
>>> import sys
>>> sys.getrefcount('abc')
68
>>> sys.getrefcount(100)
110
>>> sys.getrefcount(2)
6471

然而,一个绝对不存在的值将返回2。这与调用sys.getrefcount期间使用了对该值的几个引用有关。

1
2
>>> sys.getrefcount('nope not me.  I am definitely not here already.')
2

注意,空元组有很多引用:

1
2
>>> sys.getrefcount(tuple())
34571

但是空列表没有额外的引用:

1
2
>>> sys.getrefcount(list())
1

为什么会这样?因为元组是不可变的,所以可以跨任意数量的变量共享该值。但是,列表是可变的,因此不能在任意变量之间共享,或者对其中一个的更改会影响其他变量。

顺便说一句,这也是为什么您不能使用可变类型作为函数的默认参数值的原因。想想这个天真的小功能:

1
2
3
4
5
>>> def foo(value=[]):
...     value.append(1)
...     print(value)
...
...

当你打电话给它的时候,你可能会期望得到[1]的打印…

1
2
>>> foo()
[1]

但是,当你再次调用它时,你会探测。不会指望把[1,1]弄出去…????

1
2
>>> foo()
[1, 1]

一天又一天…

1
2
3
4
5
>>> foo()
[1, 1, 1]

>>> foo()
[1, 1, 1, 1]

为什么会这样?因为函数的默认参数在函数定义期间只计算一次,而不是在函数运行时。这样,如果您使用可变值作为默认参数值,那么您将一直使用该值,在函数被多次调用时以意外的方式发生变化。

正确的方法是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> def foo(value=None):
...     if value is None:
...         value = []
...     value.append(1)
...     print(value)
...
...
>>>
>>> foo()
[1]
>>> foo()
[1]
>>> foo()
[1]