为什么“is”运算符在算术相等的表达式中表现出乎意料

问题描述

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

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)}")

我在表达式 False 中得到了 a is b

True
False
id(a) = 3030783801968 
id(b) = 3030783802064

为什么在将表达式的结果分配给整数和带有其他变量的表达式时,变量的行为会有所不同?虽然在数学上这给出了相同的整数。

解决方法

当您执行以下操作时:

(case-1)

a = 1000
b = a

或(案例2)

a = 1000
b = 1000

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

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

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

所以在第二种情况下,ab 在技术上都是 alias1000

现在在您的示例中:

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 == 表示左边的值与右边的值相同

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

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

关键要点:每个表达式都被评估,然后比较结果。如果它们相等,则表达式为真。

什么是is

Python is,另一方面,表示左边的名字和右边的名字指向同一个对象

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。)

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

等等,还有更多

如果你自己解决这个问题,你会发现我写的内容有的异常,并让你自己感到困惑。这里只是一些:

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

什么??为什么这是真的?为了优化 Python,“最常见的数字”已经过优化。我可能是错的,但我记得内存中为最常见的数字指定了空间。这些是前几百个整数,以及其他一些。

但也有其他优化:

>>> 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
,

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

is 将在下面返回 false

id(a) = 3030783801968 <----
id(b) = 3030783802064 <----

is 将在下面返回 true

    id(a) = 2806705928816 <----
    id(b) = 2806705928816 <----
,

Python 通过将表达式的表达式一一求值来执行语句,然后对这些值执行一些操作。

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

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

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

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