可变类型的链式赋值
Chained assignment for mutable types
运行在调试一段代码时遇到这个问题。如果之前没有意识到这种行为。
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]
所以基本上这里我们有两个名称分配给同一个 variable/memory 地址。这不同于:
foo = [1, 2, 3]
bar = [1, 2 ,3]
hex(id(foo))
Out[129]: '0x1f315198448'
hex(id(bar))
Out[130]: '0x1f319567dc8'
此处更改 foo
或 bar
不会对另一个产生任何影响。
所以我的问题是:为什么这个特性(可变类型的链式赋值)甚至存在于 Python 中?除了给你搬起石头砸自己的脚的工具之外,它还有什么用吗?
对于像
这样的简单、常见的初始化很有用
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()
可以 return 可变或不可变值。分配的解析器不知道它会是什么。
有很多方法可以用多次引用可变值来搬起石头砸自己的脚,Python 不会特意阻止你。有关一些更常见的示例,请参阅 "Least Astonishment" and the Mutable Default Argument and List 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 = 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]
所以基本上这里我们有两个名称分配给同一个 variable/memory 地址。这不同于:
foo = [1, 2, 3]
bar = [1, 2 ,3]
hex(id(foo))
Out[129]: '0x1f315198448'
hex(id(bar))
Out[130]: '0x1f319567dc8'
此处更改 foo
或 bar
不会对另一个产生任何影响。
所以我的问题是:为什么这个特性(可变类型的链式赋值)甚至存在于 Python 中?除了给你搬起石头砸自己的脚的工具之外,它还有什么用吗?
对于像
这样的简单、常见的初始化很有用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()
可以 return 可变或不可变值。分配的解析器不知道它会是什么。
有很多方法可以用多次引用可变值来搬起石头砸自己的脚,Python 不会特意阻止你。有关一些更常见的示例,请参阅 "Least Astonishment" and the Mutable Default Argument and List 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 永远不会自发地复制对象,你总是必须告诉它这样做。