‘300 is 301 – 1’ 在Python中返回 ‘True’ 的原因是什么?

huangapple go评论62阅读模式
英文:

Why does '300 is 301 - 1' return 'True' in Python?

问题

为什么在Python中 300 is 301 - 1 返回 True

我本来期望Python会做一些类似于将值300和301存储在两个不同的内存位置x和y中,然后对存储在y中的值应用-1。由于300不在整数的特殊范围[-5, 256]内,这些整数具有唯一的内存位置,我不明白Python(或CPython)如何仍然可以理解它实际上可以丢弃y并添加对x的引用。

也许我上面描述的更适用于 300 is (lambda x: x - 1)(301),这确实返回 False。但那么,在指令 300 is 301 - 1 中会发生什么?

英文:

Why does 300 is 301 - 1 return True in Python?

I would have expected Python to do something like store the values 300 and 301 at two distinct memory locations x and y respectively, then apply -1 to the value stored in y. As 300 is not in the special range [-5, 256] of integers which have a unique memory location, I don't see how Python (or CPython) can still understand that it can actually just discard y and add a reference to x.

Maybe what I am describing above would rather correspond to 300 is (lambda x: x - 1)(301), which indeed returns False. But then, what happens during the instruction 300 is 301 - 1?

答案1

得分: 4

以下是翻译好的内容:

像这样的事情是实现的意外,不能依赖。在此处,根据CPython 3.11.3:

>>> 300 is 301 - 1
True
>>> one = 1
>>> 300 is 301 - one
False

事实上,第二个正是您预期的方式工作。为什么第一个不是呢?因为恰好这个版本的CPython编译器在编译时计算了301 - 1,并在代码对象的常量表中找到了相同的结果。

更详细地说:

>>> from dis import dis
>>> def f():
...     return 300 is 301 - 1
>>> dis(f)
  1           0 RESUME                   0

  2           2 LOAD_CONST               1 (300)
              4 LOAD_CONST               1 (300)
              6 IS_OP                    0
              8 RETURN_VALUE

看到了吗?它加载了相同的常量对象(整数300)作为is操作数。虽然它没有继续执行下一个步骤,但它确实可以:在编译时计算is,并将整个函数简化为仅返回True

英文:

Things like this are accidents of implementation, and must not be relied on. Here under CPython 3.11.3:

>>> 300 is 301 - 1
True
>>> one = 1
>>> 300 is 301 - one
False

The second in fact works the way you expected. Why doesn't the first? Because it just so happens that this version of the CPython compiler evaluates 301 - 1 at compile time, and finds the same result already in the code object's constant table.

In more detail:

>>> from dis import dis
>>> def f():
...     return 300 is 301 - 1
>>> dis(f)
  1           0 RESUME                   0

  2           2 LOAD_CONST               1 (300)
              4 LOAD_CONST               1 (300)
              6 IS_OP                    0
              8 RETURN_VALUE

See? It loads the same constant object (for the int 300) as the operands for is. While it doesn't go on to do this next thing, it could: evaluate the is at compile-time too, and reduce the entire function to just returning True.

答案2

得分: 3

解释器可以自由地优化不可变内置对象,没有什么"特殊范围",那只是一个实现细节。这里所发生的是常量折叠的示例。当表达式编译时,Python 简单地创建对常量的引用,并为给定的代码块缓存常量。这可以使用反汇编器轻松检查:

import dis
dis.dis("300 is 301 - 1")

你可以看到它只是加载了相同的对象进行is操作比较。

而且,如果我们编译此代码,代码对象在常量的第0个位置携带了300:

code = compile("300 is 301 - 1", "__main__", "exec")
code.co_consts

(300, None)

英文:

The interpreter is free to optimize immutable built-ins all it wants. There is no "special range", that is an implementation detail. What is going on here is an example of constant folding. When the expression is compiled, Python simply creates references to constants, and caches constants for a given block of code. This is easily introspectable using the disassembler:

>>> import dis
>>> dis.dis("300 is 301 - 1")
<dis>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
  0           0 RESUME                   0

  1           2 LOAD_CONST               0 (300)
              4 LOAD_CONST               0 (300)
              6 IS_OP                    0
              8 RETURN_VALUE

You can see it simply loads the same object for the is_op comparison.

And indeed, if we compile this code, the code object carries the 300 in the 0th position of the constants:

>>> code = compile("300 is 301 - 1", "__main__", "exec")
__main__:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
>>> code.co_consts
(300, None)

答案3

得分: 2

对操作的字节码进行分解,你会看到在这种情况下,常数是预先计算的:

>>> def f():
...  return 300 is 301 - 1
...
<stdin>:2: SyntaxWarning: "is" with a literal. Did you mean "=="?
>>> import dis
>>> dis.dis(f)
  1           0 RESUME                   0

  2           2 LOAD_CONST               1 (300)
              4 LOAD_CONST               1 (300)
              6 IS_OP                    0
              8 RETURN_VALUE
英文:

Disassemble the byte code of the operation and you'll see that in this case the constant is pre-computed:

>>> def f():
...  return 300 is 301 - 1
...
<stdin>:2: SyntaxWarning: "is" with a literal. Did you mean "=="?
>>> import dis
>>> dis.dis(f)
  1           0 RESUME                   0

  2           2 LOAD_CONST               1 (300)
              4 LOAD_CONST               1 (300)
              6 IS_OP                    0
              8 RETURN_VALUE

huangapple
  • 本文由 发表于 2023年6月1日 08:06:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76377950.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定