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

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

提问人:frost101 提问时间:2/23/2021 最后编辑:xdhmoorefrost101 更新时间:2/23/2021 访问量:94

问:

这个和这个,我仍然无法理解以下行为:

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

我进入了表情Falsea is b

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

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

python-3.x 身份 平等 cpython

评论

1赞 Arya McCarthy 2/23/2021
因为两个 1000 是字面意思。它们是在程序开始时定义的。只需要其中之一。
1赞 Scott Hunter 2/23/2021
在第二种情况下,它们不是“同一个整数”;它们是具有相同的不同整数对象。
4赞 juanpa.arrivillaga 2/23/2021
究竟什么是意想不到的?你为什么期望在这里是真实的?在你的第一个例子中,这是真的,这一事实应该令人惊讶a is b
2赞 Jan Christoph Terasa 2/23/2021
不要用于检查算术相等性,这就是用途。 用于检查两个变量/名称是否引用同一对象is==is
0赞 xdhmoore 2/23/2021
这完全是猜测,但我的猜测是,这只是编译器优化有多聪明的问题。大多数时候,你并不关心 Python 在后台如何分配变量内存,但在第一种情况下,它试图通过避免重复分配来节省空间,如果你不观察值,所有这些都对你来说是透明的。但在第二种情况下,它不够聪明,所以你最终会得到 2 个分配。id()

答:

0赞 Ade_1 2/23/2021 #1

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

对于以下情况,is 将返回 false

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

对于以下内容,is 将返回 true

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

评论

1赞 xdhmoore 2/23/2021
我可能会感到困惑,但我认为这个问题是在正确理解 和 之间的区别的情况下提出的。问题是为什么 Python 在一种情况下分配相同的 id/地址,而在另一种情况下不分配。==is
0赞 Ade_1 2/23/2021
在第二个实例中,您通过添加“a-a”进行了“附加算术”,因此,Python 然后将值存储在另一个位置
3赞 Rishabh Kumar 2/23/2021 #2

当你做这样的事情时:

(案例-1)

a = 1000
b = a

或(案例 2)

a = 1000
b = 1000

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

因此,在第一种情况下,python 在执行之前会创建一个别名。ba

第二种情况略有不同。 Python 是一种真正的面向对象语言,文字被视为一个对象。(直观地说,你可以把 1000 看作是 const 对象的名称)。1000

所以在第二种情况下,从技术上讲,两者都变成了abalias1000

现在在您的示例中:

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

虽然赋值 ,但 Python 事先并不知道 的值是多少。当我说事先时,我的意思是在开始任何形式的计算之前。因此,python 为该位置保留了一个新的内存位置,然后将操作的输出保存在这个新的内存位置。bab

还值得注意的是:

4-1 is 3
True

在这种情况下,python 不会保存此行,而是在编译之前对其进行处理,以进行运行时优化。4-13

0赞 Rambarun Komaljeet 2/23/2021 #3

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

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

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

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

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

1赞 Mike Williamson 2/23/2021 #4

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


什么?==

Python 的意思是左边的值与右边的值相同==

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

是。它需要几个计算步骤才能使每个表达式达到 的值。即便如此,整数也与浮点数进行比较,因此需要将整数最终转换为浮点True121212.0

关键要点:对每个表达式进行评估并比较结果。如果它们相等,则表达式为真。

什么?is

Python 另一方面,意味着左边的名称指向与右边的名称相同的对象is

a = 3.14159
b = a
a is b

是。 已分配给值 。但更重要的是,有一个内存块保存一个对象,在本例中是浮点数 3.14159。 指向该对象/内存块。 指向 ,这意味着它指向同一内存块Truea3.14159aba


你可以很容易地测试这一点:创建两个只指向一个数字的“名称”,然后使用 进行比较,它们将不匹配: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

这些仍然遵循与我之前所说的相同的规则:计算结果的原因是因为 并且都指向内存/对象中的相同位置。isTrueab

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

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

而不是写作

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