Python中的链式比较实际上如何工作?
可以任意链接比较,例如
x < y <= z
与等效x < y and y <= z
,不同之处在于y
比较仅被评估一次(但在两种情况下z
都x < y
被发现为假,则根本不评估)。
所以像(人为的例子):
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
是否 Val1
介于1和10之间,并且仅打印“ woo!”。 if
Val2
也在10到20之间(证明它们可以“任意链接”)。这也是有道理的。
但是我仍然很好奇在lexer / parser / compiler(或其他)级别上如何实际实现/解释这一点。
上面的第一个示例基本上是这样实现的:
x = input("Value:")
1 < x and x < 10: print "Is between 1 and 10"
在x
那些比较中真正存在的地方(实际上实际上是未命名的)?还是以某种方式使比较运算符同时返回布尔结果和正确操作数的评估(将用于进一步比较)还是类似的东西?
将分析扩展到第二个示例会使我相信,它使用的是诸如未命名中间结果之类的东西(如果有一个术语,有人会教我),因为它在进行比较之前不会评估所有操作数。
-
您可以简单地让Python告诉您
dis
模块产生了什么字节码:>>> 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
字节码堆(上用途项目input
的全球和'Value:'
字符串)调用函数用一个参数,更换堆栈函数调用的结果在这两个项目。在函数调用之前,常量1
已加载到堆栈中。因此,在
input
被调用时,堆栈看起来像:input_result 1
并
DUP_TOP
复制最高值,然后旋转最高的三个堆栈值以得出:1 input_result input_result
和
COMPARE_OP
用来测试前两项<
,并用结果替换前两项。如果结果是字节码跳转到27,
False
则JUMP_IF_FALSE_OR_POP
字节码将False
顶部的剩余部分旋转到顶部,input_result
用a清除该字节POP_TOP
,然后返回剩余的False
顶部值作为结果。True
但是,如果结果是该值,则该JUMP_IF_FALSE_OR_POP
字节码会从堆栈中弹出该值,并将其放置10
在顶部,我们得到:10 input_result
然后进行另一个比较并返回。
综上所述,基本上,Python会这样做:
stack_1 = stack_2 = input('Value:') if 1 < stack_1: result = False else: result = stack_2 < 10
与
stack_*
值再次清除。然后,堆栈保存 未命名的中间结果 以进行比较