为什么 'is' 运算符在算术相等的表达式中出现意外行为

Why does the 'is' operator behave unexpectedly with arithmetically equal expressions

阅读 and 后,我仍然无法理解以下行为:

a = 1000
b = 1000
print (a == b)
print (a is b)
print (f"id(a) = {id(a)} \nid(b) = {id(b)}")

不出所料

True
True
id(a) = 2806705928816 
id(b) = 2806705928816

但是当我尝试做这样的事情时:

a = 1000
b = 1000 + a - a
print (a == b)
print (a is b)
print (f"id(a) = {id(a)} \nid(b) = {id(b)}")

我在表达式 a is b

中得到了 False
True
False
id(a) = 3030783801968 
id(b) = 3030783802064

为什么在将表达式的结果赋值给整数和将表达式的结果赋给其他变量时,变量的行为会有所不同?尽管在数学上这给出了相同的整数。

区别在于对位置的引用。 '==' 检查数据类型和值是否相等,但是,'is;引用变量在内存中的位置。

以下

将return错误
id(a) = 3030783801968 <----
id(b) = 3030783802064 <----

以下

将return为真
    id(a) = 2806705928816 <----
    id(b) = 2806705928816 <----

当你做类似的事情时:

(案例 1)

a = 1000
b = a

或(案例 2)

a = 1000
b = 1000

Python 足够聪明,可以事先知道即使在执行之后你也不需要新的内存。

因此,python 在执行之前使 b 在第一种情况下成为 a 的别名。

第二种情况有点不同。 Python 是真正的面向对象语言,文字 1000 被视为对象。 (直觉上你可以认为 1000 是一个 const 对象的名称)。

所以在第二种情况下,ab 从技术上讲,都变成了 1000

alias

现在在你的例子中:

a = 1000
b = 1000 + a - a
print (a == b)
print (a is b)

虽然 b 的赋值,但 python 事先并不知道 a 的值是什么。当我说事前时,我的意思是在开始任何形式的计算之前。所以python为b保留一个新的内存位置,然后将操作的输出保存在这个新的内存位置。

另外值得注意的是:

4-1 is 3
True

在这种情况下,python 不会用 4-1 保存这一行,而是在编译之前将其处理为 3,以进行运行时优化。

Python 通过将其表达式逐一计算为值来执行语句,然后对这些值执行一些操作。

来源: https://courses.cs.washington.edu/courses/cse140/13wi/eval_rules.pdf

基本上 b = 1000 + a - a 不是一次性完成的,而是在多次评估中完成的,并且 python 将每次评估时 b 的结果存储在与 a 不同的内存位置。此时a和b是不同的对象。

使用 == 进行相等性检查。

使用“is”检查对象是否相同(变量引用相同的内存位置)。

您已经有了一些准确的答案。在这里我给出一个“返璞归真”的回答。


什么是 ==

Python ==表示左边的值与右边的值是否相同.

sum([5, 7]) == (48 * 3)**0.5

True。它需要几个评估步骤才能使每个表达式达到 12 的值。即便如此,integer 12 仍在与 float 12.0 进行比较,因此最终将整数转换为需要一个浮点数。

要点:每个 表达式 都被 求值 并且比较结果 。如果它们相等,则表达式为真。

什么是 is

另一方面,

Pythonis表示是左边的名字指向与右边的名字相同的对象

a = 3.14159
b = a
a is b

Truea 已分配给值 3.14159。但更重要的是,有一块内存包含一个对象,在本例中是浮点数 3.14159。 a 指向那个对象/内存块。 b 指向 a,这意味着它指向 相同的内存块


你可以很容易地测试这个:创建两个简单地指向一个数字的“名称”,然后使用 is 比较它们,它们将不匹配:

>>> a = 1239481203948
>>> b = 1239481203948
>>> a is b
False

这是错误的,因为我们现在在内存/对象中有两个不同的位置指向它们中的每一个:

>>> id(a)
140402381635344
>>> id(b)
140402391174416

(在你的机器上,你会得到一组不同的 ids。)

因此,实际上,您已经“浪费”了 space,因为您有两个对象占用了 space 相同的信息。

等等,还有更多

如果您自己尝试一下,您会发现 我写的内容有例外,并且让您自己感到困惑。这里只是一些:

>>> a = 157
>>> b = 157
>>> a is b
True

什么??为什么这是真的?优化Python,优化了“最常见的数字”。我可能错了,但我记得在内存中为最常见的数字指定了 space。这些是前几百个整数,还有其他几个。

但还有其他优化:

>>> a = None
>>> b = None
>>> a is b
True

>>> a = True
>>> b = True
>>> a is b
True

这些仍然遵循我之前所说的相同规则:is 计算为 True 的原因是因为 ab 都指向内存/对象中的相同位置。

由于 Python 中的优化,在这些奇怪的情况下会发生这种情况。 但一般来说,确保 is 计算结果为 True 的唯一方法是将名称分配给已经具有名称 的对象,就像我们写的那样:

>>> a = 3.14159
>>> b = a
>>> a is b
True

而不是写作

>>> a = 3.14159
>>> b = 3.14159
>>> a is b
False