【问题标题】:Chained assignment for mutable types可变类型的链式赋值
【发布时间】:2021-02-11 10:42:21
【问题描述】:

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

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 中?除了给你工具让你在脚上开枪之外,它还有其他用途吗?

【问题讨论】:

  • foo = bar = [1, 2, 3]的行为与bar = [1, 2, 3]后跟foo = bar的行为一致。
  • @MarkMeyer,同意。但是你为什么要知道对 foo 的任何更改也会更改 bar 呢?将两个名称分配给同一个变量有什么意义?
  • 我不知道。我很确定我从未在现实生活中使用过这种分配模式。另一方面,我真的不希望我的代码在没有明确制作副本的情况下分配(可能)大型集合。
  • 如果你调用list.sort(foo),你宁愿希望list.sort函数持有与foo相同的列表的引用,而不是它的副本;否则它将对自己的副本进行排序,foo 保持不变。但当然list.sort 中必须有一个局部变量,它是一个不同于foo 的变量(它可能被称为self)。因此,您希望两个不同的变量引用同一个可变对象的原因有很多。

标签: python chained-assignment


【解决方案1】:

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

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" and the Mutable Default ArgumentList of lists changes reflected across sublists unexpectedly

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

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

而不是

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

How do chained assignments work?

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

【讨论】:

  • 这适用于不可变对象,因为它们实际上是指针。如果您执行foo += 1,那么foo 现在将指向不同的内存地址,而barbaz 仍将指向存储值0 的原始地址。它永远不会导致不可变类型出现问题,但可变类型是另一回事。
  • 我不确定我是否同意“解析器无法判断最后的表达式是可变类型还是不可变类型”。如果解析器在赋值的另一边看到[ ]{ },它应该立即意识到它正在处理一个可变的。
  • foo = bar = baz = somefunc() 取决于somefunc() 返回的是可变对象还是不可变对象。
  • foo = bar = baz = [] if f() else 0
  • 如果f() 被定义并且可以返回一个值解析器应该知道返回值的类型,不是吗?
猜你喜欢
  • 2019-10-29
  • 1970-01-01
  • 1970-01-01
  • 2020-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-24
  • 2023-03-10
相关资源
最近更新 更多