为什么python列表在使用=运算符时会这样做

为什么python列表在使用=运算符时会这样做,python,list,Python,List,为什么会出现以下代码: a = [1,2,3] b = a b[0] = 3 print(a) a = [1,2,3] b = a b = [0,0,0] print(a,b) 更改后是否打印列表b?[3,2,3]。 为什么这是真的,但以下代码: a = [1,2,3] b = a b[0] = 3 print(a) a = [1,2,3] b = a b = [0,0,0] print(a,b) 打印[1,2,3][0,0,0]??这似乎不一致。如果第一个代码为true,那么第二个代码

为什么会出现以下代码:

a = [1,2,3]
b = a
b[0] = 3
print(a)
a = [1,2,3]
b = a
b = [0,0,0]
print(a,b)
更改后是否打印列表b?[3,2,3]。 为什么这是真的,但以下代码:

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

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

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

当你说:

a = [1,2,3]
b = a
您已经在内存中创建了一个可变列表,分配
a
指向它,然后分配
b
指向它。在记忆中也是一样的

因此,当您对其进行变异(修改)时:

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

但是,当您更换它时:

b = [0,0,0]
它在内存中创建一个新的可变列表,并指定
b
指向它


查看
id()
函数。它将告诉您任何变量的“地址”。您可以使用
id(varname)
查看哪些名称指向相同的内存位置


奖励:python中的每个值都通过引用传递。。。也就是说,当您将它赋给一个变量时,它只会使该变量指向内存中的那个值。拥有不可变类型允许python对公共不可变类型“重用”相同的内存位置

当解释器启动时,考虑一些常见值:

>>> import sys
>>> sys.getrefcount('abc')
68
>>> sys.getrefcount(100)
110
>>> sys.getrefcount(2)
6471
但是,绝对不存在的值将返回
2
。这与调用
sys.getrefcount

>>> sys.getrefcount('nope not me.  I am definitely not here already.')
2
请注意,空元组有很多引用:

>>> sys.getrefcount(tuple())
34571
>>> sys.getrefcount(list())
1
但空列表没有额外的引用:

>>> sys.getrefcount(tuple())
34571
>>> sys.getrefcount(list())
1
这是为什么?因为元组是不可变的,所以可以跨任意数量的变量共享该值。但是,列表是可变的,因此不能跨任意变量共享,否则对其中一个变量的更改会影响其他变量

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

>>> def foo(value=[]):
...     value.append(1)
...     print(value)
...
...
当您调用它时,您可能希望打印出
[1]

>>> foo()
[1]
然而,当你再次呼叫它时,你可能会。不希望得到
[1,1]
输出

>>> foo()
[1, 1]
而且不停地

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

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

正确的方法是:

>>> def foo(value=None):
...     if value is None:
...         value = []
...     value.append(1)
...     print(value)
...
...
>>>
>>> foo()
[1]
>>> foo()
[1]
>>> foo()
[1]

在第一种情况下,您正在重新分配一个特定元素(
b[0]
),因此它仍然引用初始的
a
列表。在第二种情况下,您将使用新列表完全重新分配/覆盖它,因此对
a
的初始引用将被覆盖。您可能会发现这有助于理解其背后的概念。
b=a
不会创建
a
的单独副本。相反,
a
b
现在都引用相同的值。把它想象成一个标签或名牌;你可以带着写着“嗨,我是鲍勃”的名牌和另一个写着“嗨,我是史蒂夫”的名牌在聚会上四处走动。鲍勃和史蒂夫都是同一个人的名字——你。