简而言之:
正如参考手册所述:
块是作为一个单元执行的Python程序文本。
以下是块:模块、函数体和类定义。
每个交互式输入的命令都是一个块。
这就是为什么在函数的情况下,您有一个单一的代码块,其中包含用于数字文字1000
的单个对象,因此id(a) == id(b)
将返回True
。
在第二种情况下,您有两个不同的代码对象,每个对象都有自己不同的1000
文字对象,因此id(a) != id(b)
。
请注意,这种行为不仅适用于
int
字面量,例如
float
字面量也会得到类似的结果(请参见
此处)。
当然,比较对象(除了显式的
is None
测试)应该始终使用等号运算符
==
而不是
is
。
这里提到的所有内容都适用于Python的最流行实现CPython。其他实现可能有所不同,因此在使用它们时不应做出任何假设。
较长的回答:
为了更清晰地了解和验证这种看似奇怪的行为,我们可以直接查看每种情况下的代码
对象,并使用dis
模块进行验证。
对于函数func
:
除了所有其他属性外,函数对象还有一个__code__
属性,允许您窥视该函数的已编译字节码。使用dis.code_info
,我们可以获得给定函数的代码对象中存储的所有属性的漂亮清晰的视图:
>>> print(dis.code_info(func))
Name: func
Filename: <stdin>
Argument count: 0
Kw-only arguments: 0
Number of locals: 2
Stack size: 2
Flags: OPTIMIZED, NEWLOCALS, NOFREE
Constants:
0: None
1: 1000
Variable names:
0: a
1: b
我们只关心函数
func
的
Constants
条目。在其中,我们可以看到有两个值:
None
(始终存在)和
1000
。我们只有一个代表常量
1000
的实例。这是在调用函数时将分配给
a
和
b
的值。
通过
func.__code__.co_consts[1]
很容易访问此值,因此,在函数中查看
a is b
评估的另一种方法如下:
>>> id(func.__code__.co_consts[1]) == id(func.__code__.co_consts[1])
当然,这将计算为True
,因为我们引用的是同一个对象。
对于每个交互式命令:
如前所述,每个交互式命令都被解释为单个代码块:独立地解析、编译和评估。
我们可以通过compile
内置函数获取每个命令的代码对象:
>>> com1 = compile("a=1000", filename="", mode="single")
>>> com2 = compile("b=1000", filename="", mode="single")
对于每个赋值语句,我们将得到一个类似以下代码对象的外观:
>>> print(dis.code_info(com1))
Name: <module>
Filename:
Argument count: 0
Kw-only arguments: 0
Number of locals: 0
Stack size: 1
Flags: NOFREE
Constants:
0: 1000
1: None
Names:
0: a
com2
的同一命令看起来相同,但是有一个根本性的区别: 每个代码对象com1
和com2
具有不同的int实例,代表字面意义上的1000
。这就是为什么在这种情况下,当我们通过co_consts
参数执行a is b
时,实际上得到的是:
>>> id(com1.co_consts[0]) == id(com2.co_consts[0])
False
这与我们实际得到的相符。
不同的代码对象,不同的内容。
注意:我对源代码中的这个过程有些好奇,经过挖掘后,我相信我终于找到了它。在编译阶段,
co_consts
属性由字典对象表示。在
compile.c
中,我们实际上可以看到初始化过程:
/* snippet for brevity */
u->u_lineno = 0
u->u_col_offset = 0
u->u_lineno_set = 0
u->u_consts = PyDict_New()
/* snippet for brevity */
在编译期间,会检查已存在的常量。请参阅下面
@Raymond Hettinger's answer中的更多信息。
注意事项:
Chained statements will evaluate to an identity check of True
It should be more clear now why exactly the following evaluates to True
:
>>> a = 1000
>>> a is b
In this case, by chaining the two assignment commands together we tell the interpreter to compile these together. As in the case for the function object, only one object for the literal 1000
will be created resulting in a True
value when evaluated.
Execution on a module level yields True
again:
As previously mentioned, the reference manual states that:
... The following are blocks: a module ...
So the same premise applies: we will have a single code object (for the module) and so, as a result, single values stored for each different literal.
The same doesn't apply for mutable objects:
意思是,除非我们明确地将可变对象初始化为相同的对象,否则这些对象的身份永远不会相等,例如:
a = b = []
,这样做才能使它们的身份相等。
a = []
a is b
在文档中再次指定:
在 a = 1; b = 1之后,a和b可能引用具有值为1的相同对象,也可能不引用,这取决于实现方式,但是在 c = []; d = []之后,c和d保证引用两个不同的、唯一的、新创建的空列表。
is
感觉不太对。 - Martin Bonner supports Monica