Python 中的链式比较实际上是如何工作的?
How do chained comparisons in Python actually work?
Comparisons can be chained arbitrarily, e.g., x < y <= z
is equivalent to x < y and y <= z
, except that y
is evaluated only once (but in both cases z
is not evaluated at all when x < y
is found to be false).
这些 SO questions/answers 进一步说明了这种用法:
- Python comparison operators chaining/grouping left to right?
- What does "evaluated only once" mean for chained comparisons in Python?, in particular the currently-accepted answer
所以像(人为的例子):
if 1 < input("Value:") < 10: print "Is greater than 1 and less than 10"
只要求输入一次。这是有道理的。还有这个:
if 1 < input("Val1:") < 10 < input("Val2:") < 20: print "woo!"
仅要求 Val2
if Val1
介于 1 和 10 之间并且仅打印 "woo!" if Val2
也在10到20之间(证明它们可以是'chained arbitrarily')。这也有道理。
但我仍然很好奇这实际上是如何 implemented/interpreted 在 lexer/parser/compiler(或其他)级别。
上面第一个例子是不是基本上是这样实现的:
x = input("Value:")
1 < x and x < 10: print "Is between 1 and 10"
其中 x
真的只存在(并且实际上基本上未命名)用于这些比较?或者它是否以某种方式使比较运算符 return 既是布尔结果又是正确操作数的评估(用于进一步比较)或类似的东西?
将分析扩展到第二个示例使我相信它使用的是类似未命名的中间结果的东西(如果有这个术语,请告诉我)因为它在进行比较之前不会评估所有操作数。
你可以简单地让 Python 告诉你用 dis
module:
产生的字节码是什么
>>> import dis
>>> def f(): return 1 < input("Value:") < 10
...
>>> dis.dis(f)
1 0 LOAD_CONST 1 (1)
3 LOAD_GLOBAL 0 (input)
6 LOAD_CONST 2 ('Value:')
9 CALL_FUNCTION 1
12 DUP_TOP
13 ROT_THREE
14 COMPARE_OP 0 (<)
17 JUMP_IF_FALSE_OR_POP 27
20 LOAD_CONST 3 (10)
23 COMPARE_OP 0 (<)
26 RETURN_VALUE
>> 27 ROT_TWO
28 POP_TOP
29 RETURN_VALUE
Python 使用堆栈; CALL_FUNCTION
bytecode 使用堆栈上的项目(input
全局变量和 'Value:'
字符串)调用带有一个参数的函数,用函数的结果替换堆栈上的这两项称呼。在函数调用之前,常量 1
被加载到堆栈上。
所以当 input
被调用时,堆栈看起来像:
input_result
1
和 DUP_TOP
duplicates the top value, before rotating the top three stack 要达到的值:
1
input_result
input_result
和 COMPARE_OP
用 <
测试前两项,用结果替换前两项。
如果该结果是 False
,则 JUMP_IF_FALSE_OR_POP
bytecode 跳转到 27,这会将 False
旋转到顶部,剩余的 input_result
以使用 POP_TOP
,然后 return 剩下的 False
个最高值作为结果。
但是,如果结果 True
,该值会被 JUMP_IF_FALSE_OR_POP
字节码从堆栈中弹出,并且 10
值会被加载到顶部,我们得到:
10
input_result
并进行了另一个比较,然后 returned 代替。
总而言之,基本上 Python 然后这样做:
stack_1 = stack_2 = input('Value:')
if 1 < stack_1:
result = False
else:
result = stack_2 < 10
再次清除 stack_*
值。
然后,堆栈保存未命名的中间结果以比较
Comparisons can be chained arbitrarily, e.g.,
x < y <= z
is equivalent tox < y and y <= z
, except thaty
is evaluated only once (but in both casesz
is not evaluated at all whenx < y
is found to be false).
这些 SO questions/answers 进一步说明了这种用法:
- Python comparison operators chaining/grouping left to right?
- What does "evaluated only once" mean for chained comparisons in Python?, in particular the currently-accepted answer
所以像(人为的例子):
if 1 < input("Value:") < 10: print "Is greater than 1 and less than 10"
只要求输入一次。这是有道理的。还有这个:
if 1 < input("Val1:") < 10 < input("Val2:") < 20: print "woo!"
仅要求 Val2
if Val1
介于 1 和 10 之间并且仅打印 "woo!" if Val2
也在10到20之间(证明它们可以是'chained arbitrarily')。这也有道理。
但我仍然很好奇这实际上是如何 implemented/interpreted 在 lexer/parser/compiler(或其他)级别。
上面第一个例子是不是基本上是这样实现的:
x = input("Value:")
1 < x and x < 10: print "Is between 1 and 10"
其中 x
真的只存在(并且实际上基本上未命名)用于这些比较?或者它是否以某种方式使比较运算符 return 既是布尔结果又是正确操作数的评估(用于进一步比较)或类似的东西?
将分析扩展到第二个示例使我相信它使用的是类似未命名的中间结果的东西(如果有这个术语,请告诉我)因为它在进行比较之前不会评估所有操作数。
你可以简单地让 Python 告诉你用 dis
module:
>>> import dis
>>> def f(): return 1 < input("Value:") < 10
...
>>> dis.dis(f)
1 0 LOAD_CONST 1 (1)
3 LOAD_GLOBAL 0 (input)
6 LOAD_CONST 2 ('Value:')
9 CALL_FUNCTION 1
12 DUP_TOP
13 ROT_THREE
14 COMPARE_OP 0 (<)
17 JUMP_IF_FALSE_OR_POP 27
20 LOAD_CONST 3 (10)
23 COMPARE_OP 0 (<)
26 RETURN_VALUE
>> 27 ROT_TWO
28 POP_TOP
29 RETURN_VALUE
Python 使用堆栈; CALL_FUNCTION
bytecode 使用堆栈上的项目(input
全局变量和 'Value:'
字符串)调用带有一个参数的函数,用函数的结果替换堆栈上的这两项称呼。在函数调用之前,常量 1
被加载到堆栈上。
所以当 input
被调用时,堆栈看起来像:
input_result
1
和 DUP_TOP
duplicates the top value, before rotating the top three stack 要达到的值:
1
input_result
input_result
和 COMPARE_OP
用 <
测试前两项,用结果替换前两项。
如果该结果是 False
,则 JUMP_IF_FALSE_OR_POP
bytecode 跳转到 27,这会将 False
旋转到顶部,剩余的 input_result
以使用 POP_TOP
,然后 return 剩下的 False
个最高值作为结果。
但是,如果结果 True
,该值会被 JUMP_IF_FALSE_OR_POP
字节码从堆栈中弹出,并且 10
值会被加载到顶部,我们得到:
10
input_result
并进行了另一个比较,然后 returned 代替。
总而言之,基本上 Python 然后这样做:
stack_1 = stack_2 = input('Value:')
if 1 < stack_1:
result = False
else:
result = stack_2 < 10
再次清除 stack_*
值。
然后,堆栈保存未命名的中间结果以比较