tl; dr:
如参考手册所述:
块是作为单元执行的一段Python程序文本。以下是块:模块,函数体和类定义。 交互键入的每个命令都是一个块。
这就是为什么,在功能的情况下,你有一个 单一的 代码模块包含一个 单一 的数字文本对象
1000,所以
id(a) ==id(b)会产生
True。
在第二种情况下,您有 两个不同的代码对象, 每个 对象 都有它们自己的文字对象
1000so
id(a) != id(b)。
请注意,这种行为并不仅
int限于文字,您还会得到类似的结果,例如,
float文字。
当然,比较对象(除显式
is None测试外)应始终使用等于运算符
==而 不是
is。
这里所说的一切都适用于最流行的Python CPython实现。 其他实现可能会有所不同,因此在使用它们时不做任何假设。
更长的答案:
为了获得更清晰的视图并验证这种 看似奇怪的
行为,我们可以
pre使用
dis模块针对每种情况直接在对象中查找。
对于功能func
:
除所有其他属性外,函数对象还具有一个
__pre__属性,可让您查看该函数的已编译字节码。使用
dis.pre_info我们可以获得给定功能的代码对象中所有存储的属性的漂亮视图:
>>> print(dis.pre_info(func))Name: funcFilename: <stdin>Argument count: 0Kw-only arguments: 0Number of locals: 2Stack size: 2Flags: OPTIMIZED, NEWLOCALS, NOFREEConstants: 0: None 1: 1000Variable names: 0: a 1: b
我们只对
Constantsfunction的条目感兴趣
func。在其中,我们可以看到我们有两个值
None(始终存在)和
1000。我们只有一个
单一的 ,代表的整型实例
1000。这是该值
a和
b将要分配被调用函数时。
可以很容易地访问此值
func.__pre__.co_consts[1],因此,
a is b在函数中查看我们的评估的另一种方式如下:
>>> id(func.__pre__.co_consts[1]) == id(func.__pre__.co_consts[1])
当然,
True由于我们指的是同一对象,因此得出的结果是。
对于每个交互式命令:
如前所述,每个交互式命令都被解释为单个代码块:分别进行分析,编译和评估。
我们可以通过
compile内置的命令获取每个命令的代码对象:
>>> com1 = compile("a=1000", filename="", mode="single")>>> com2 = compile("b=1000", filename="", mode="single")对于每个赋值语句,我们将获得一个外观类似的代码对象,如下所示:
>>> print(dis.pre_info(com1))Name: <module>Filename: Argument count: 0Kw-only arguments: 0Number of locals: 0Stack size: 1Flags: NOFREEConstants: 0: 1000 1: NoneNames: 0: a
同样的命令
com2看起来一样,但 有一个根本的区别
:每个代码的对象
com1,并
com2具有代表字面不同INT实例
1000。这就是为什么在这种情况下,当我们
a isb通过
co_consts参数进行操作时,实际上得到:
>>> id(com1.co_consts[0]) == id(com2.co_consts[0])False
这与我们实际得到的一致。
不同的代码对象,不同的内容。
注意: 我对源代码中到底发生了什么感到有些好奇,在深入研究后,我相信我终于找到了它。
在编译阶段,该co_consts
属性由字典对象表示。在
compile.c实际上我们可以看到初始化:
u->u_lineno = 0;u->u_col_offset = 0;u->u_lineno_set = 0;u->u_consts = PyDict_New();
在编译期间,将检查是否已存在常量。有关更多信息,请参见下面的@Raymond
Hettinger的答案。
注意事项:
- 链接的语句将评估为的身份检查
True
现在应该更清楚地说明以下内容为何准确地得出
True:
>>> a = 1000; b = 1000;>>> a is b
在这种情况下,通过将两个分配命令链接在一起,我们告诉解释器将它们 一起
编译。与功能对象的情况一样,将只为文字
1000创建一个对象,并
True在评估时产生一个值。
- 在模块级别执行将
True
再次产生:
如前所述,参考手册指出:
…以下是块: 一个模块…
因此,适用相同的前提:我们将有一个代码对象(用于模块),因此,将为每个不同的文字存储单个值。
- 同样 不 适用于 可变 对象:
这意味着除非我们显式初始化为同一可变对象(例如,使用a = b = []),否则对象的身份永远不会相等,例如:
a = []; b = []a is b # always returns false
同样,在文档中,指定了以下内容:
在a = 1之后;b = 1,取决于实现,a和b可以或可以不使用值1引用同一对象,但是在c = []之后;d =
[],保证c和d引用两个不同的,唯一的,新创建的空列表。



