为啥python列表在使用=运算符时会像这样[重复]

Posted

技术标签:

【中文标题】为啥python列表在使用=运算符时会像这样[重复]【英文标题】:Why do python lists act like this when using the = operator [duplicate]为什么python列表在使用=运算符时会像这样[重复] 【发布时间】:2019-04-07 16:01:33 【问题描述】:

下面的代码怎么来的:

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

列表 b 被修改后会打印出来吗?[3,2,3]。 还有为什么这是真的,但下面的代码:

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] 吗?有人可以解释一下吗?

【问题讨论】:

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

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

当你说:

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

您在内存中创建了一个可变列表,分配a 指向它,然后分配b 指向它。记忆中也是一样。

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

b[0] = 3

它是索引[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

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

顺便说一句,这也是为什么你必须永远不要使用可变类型作为函数的默认参数值。考虑一下这个无辜的小功能:

>>> 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]

【讨论】:

以上是关于为啥python列表在使用=运算符时会像这样[重复]的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在解析 XML 时会从此方法中得到重复记录?

为啥 Python for 循环在遍历列表副本并进行删除时会跳过元素? [复制]

为啥我在使用 pip install 命令时会收到此错误 [重复]

为啥 MySQL 在使用带有通配符的 LIKE 运算符时会忽略假名敏感?

为啥python中有不相等的运算符[重复]

为啥 C# 'is' 运算符在比较两个布尔值时会给出正确的结果,我应该使用它吗?