1

调试一段代码时遇到了这个问题。如果以前没有意识到这种行为。

foo = bar = [1, 2, 3]

hex(id(foo))
Out[121]: '0x1f315dafe48'
hex(id(bar))
Out[122]: '0x1f315dafe48'

两个“变量”都指向同一个内存位置。但是现在如果一个改变了,另一个也改变了:

foo.append(4)

bar
Out[126]: [1, 2, 3, 4]

所以基本上在这里我们有两个名称分配给同一个变量/内存地址。这不同于:

foo = [1, 2, 3]
bar = [1, 2 ,3]
hex(id(foo))
Out[129]: '0x1f315198448'
hex(id(bar))
Out[130]: '0x1f319567dc8'

在这里更改其中一个foobar不会对另一个产生任何影响。

所以我的问题是:为什么这个特性(可变类型的链式赋值)甚至存在于 Python 中?除了给你工具让你在脚上开枪之外,它还有其他用途吗?

4

1 回答 1

2

它对于简单、常见的初始化很有用,比如

foo = bar = baz = 0

所以你不必写

foo = 0
bar = 0
baz = 0

因为它是一个语法特性,所以让它只适用于不可变类型是不可行的。解析器无法判断最后的表达式是可变类型还是不可变类型。你可以有

def initial_value():
    if random.choice([True, False]):
        return []
    else:
        return 0

foo = bar = baz = initial_value()

initial_value()可以返回一个可变或不可变的值。分配的解析器不知道它会是什么。

有很多方法可以通过多次引用可变值来打击自己,Python 不会竭尽全力阻止你。对于一些更常见的示例,请参阅“Least Astonishment”以及 Mutable Default ArgumentList of lists 更改意外地反映在子列表中

您只需要记住,在链式赋值中,值表达式只计算一次。所以你的任务相当于

temp = [1, 2, 3]
foo = temp
bar = temp

而不是

foo = [1, 2, 3]
bar = [1, 2, 3]

请参阅链式作业如何工作?

要记住的更一般的规则是 Python 从不自发地复制对象,你总是必须告诉它这样做。

于 2020-10-29T01:08:44.607 回答