Python3多重分配和内存地址
在阅读了和我的问题非常相似的内容后,我仍然无法理解以下行为:Python3多重分配和内存地址,python,python-3.x,multiple-assignment,Python,Python 3.x,Multiple Assignment,在阅读了和我的问题非常相似的内容后,我仍然无法理解以下行为: a = 257 b = 257 print(a is b) #False a, b = 257, 257 print(a is b) #True 在打印id(a)和id(b)时,我可以看到,在单独的行中分配值的变量具有不同的id,而在多个分配中,两个值具有相同的id: a = 257 b = 257 print(id(a)) #139828809414512 print(id(b)) #139828809414224 a, b =
a = 257
b = 257
print(a is b) #False
a, b = 257, 257
print(a is b) #True
在打印id(a)
和id(b)
时,我可以看到,在单独的行中分配值的变量具有不同的id,而在多个分配中,两个值具有相同的id:
a = 257
b = 257
print(id(a)) #139828809414512
print(id(b)) #139828809414224
a, b = 257, 257
print(id(a)) #139828809414416
print(id(b)) #139828809414416
但不可能通过多次赋值总是创建指向同一id的指针来解释这种行为,因为:
a, b = -1000, -1000
print(id(a)) #139828809414448
print(id(b)) #139828809414288
是否有明确的规则来解释变量何时获得相同的id
编辑
相关信息:此问题中的代码是在交互模式(ipython3)下运行的。任何此类规则都是特定于实现的。例如,CPython为小整数(-5到256)预先分配
int
对象作为性能优化
唯一的一般规则是假设任何文字的使用都将生成一个新对象。这是因为pythons解释器在加载常量值期间在
解包序列时进行了优化。当python在解包过程中遇到iterable时,它不会多次加载重复的对象,而是保留第一个对象并将所有重复的变量名分配给一个指针(在CPython实现中)。因此,所有变量都将成为对一个对象的相同引用。在python级别,您可以将此行为视为使用字典作为名称空间,而不保留重复的键
换句话说,您的解包相当于以下命令:
a = b = 257
关于负数,在Python2.X中没有任何区别,但在Python3.X中,对于小于-5的数字,python将在解包期间创建新对象:
>>> a, b = -6, -6
>>> a is b
False
>>> a, b = -5, -5
>>>
>>> a is b
True
这是由于字节码编译器中的不断折叠优化。当字节码编译器编译一批语句时,它会跟踪所看到的常量。此dict自动合并任何等效常量
以下是负责记录和编号常量的例行程序(以及一些相关职责):
在脚本中,所有顶级语句都是一个批处理,因此:
在脚本中,这将打印True
函数的代码与函数外部的代码分开跟踪其常量,这限制了常量折叠:
a = 257
def f():
b = 257
print a is b
f()
,这会打印出False
数字是257
,你也可以通过使用更大的整数看到这种行为。我总是忘记确切的范围。不是“将生成”,而是“可能生成”。你的答案如何解释我问题的最后一部分,a,b=-1000,-1000?@isternberg在上一部分中,您已经分别创建了变量,但python3中的负数解包还有另一点,我会尽快更新答案,说明原因。“一个加载常数和两个存储速度”——如果您是正确的,LOAD\u常量
正在加载一个常量元组
,该元组包含两个项。我不确定这是如何解释这两个值具有相同ID的(当它们超出python内部整数的正常范围时)!这是完全错误的<代码>解包顺序
与此无关。您将看到相同的加载常数和解包序列行为,但是a
和b
明显不同。您也可以避免优化,而且UNPACK_SEQUENCE
不会合并常量。@Kasramvd,thx,感谢您告诉我问题中的错误。我修复了它。请查看[id(I)for I in(10001000)]
:-)注意:行为不一致,因为它根本不重要。如果有任何理由关心两个257
s是否是同一个对象,那么它将有一个简单、合理的模式。窥视孔优化,与它的工作方式无关,因为它从来都不是你会或应该依赖的。谢谢。我将编辑我的问题以明确它是在交互模式下测试的。那么在python 3中小于-5
的整数呢?例如a,b=-6,-6
@Kasramvd:编译器将-6
编译成加载常量和一元负数
;有一个负责将其转换为加载一个-6
的加载常量。该优化实际上没有使用dict.@user2357112,但我在C代码中没有发现小于-5的限制(这是否与负数以2的补码的一种变体表示有关,它给人一种无限符号位串向左延伸的错觉?@Kasramvd:-5
由另一种优化处理,即小整数池。所有-5
都是相同的对象,无论您如何或在何处创建它们。
>>> a = 1000
>>> b = 1000
>>> a is b
False
>>> a = 1000; b = 1000 # 1 batch
>>> a is b
True
a = 257
b = 257
print a is b
a = 257
def f():
b = 257
print a is b
f()