Python中的链式比较实际上如何工作?

joh*_*nny 9 python comparison-operators python-internals

Python的文件进行比较说:

比较可以任意链接,例如,x < y <= z等同于x < y and y <= z,除了y仅评估一次(但在两种情况下z都没有被评估,当x < y发现是假的时).

这些SO问题/答案对这种用法有了更多的了解:

所以像(人为的例子):

if 1 < input("Value:") < 10: print "Is greater than 1 and less than 10"
Run Code Online (Sandbox Code Playgroud)

只要求输入一次.这是有道理的.还有这个:

if 1 < input("Val1:") < 10 < input("Val2:") < 20: print "woo!"
Run Code Online (Sandbox Code Playgroud)

只询问Val2 是否 Val1介于1和10之间,只打印"呜!" 如果 Val2也是10到20之间(证明它们可以'任意链接').这也是有道理的.

但我仍然很好奇这是如何在词法分析器/解析器/编译器(或其他)级别实际实现/解释的.

上面的第一个例子基本上是这样实现的:

x = input("Value:")
1 < x and x < 10: print "Is between 1 and 10"
Run Code Online (Sandbox Code Playgroud)

这里x真的只存在(实际上本质上是未命名)对于那些比较?或者它是否以某种方式使比较运算符返回布尔结果和右操作数的评估(用于进一步比较)或类似的东西?

将分析扩展到第二个例子让我相信它正在使用类似于未命名的中间结果(有人教育我,如果有一个术语),因为它不会在进行比较之前评估所有操作数.

Mar*_*ers 12

您可以简单地让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        
Run Code Online (Sandbox Code Playgroud)

Python使用堆栈; 该CALL_FUNCTION字节码使用堆栈(上项input全球和'Value:'字符串)调用函数用一个参数,更换堆栈函数调用的结果在这两个项目.在函数调用之前,常量1已加载到堆栈上.

所以当input被称为堆栈时看起来像:

input_result
1
Run Code Online (Sandbox Code Playgroud)

并且DUP_TOP旋转前三个堆栈值之前复制顶部值以达到:

1
input_result
input_result
Run Code Online (Sandbox Code Playgroud)

和a COMPARE_OP测试前两个项目<,用结果替换前两个项目.

如果结果是FalseJUMP_IF_FALSE_OR_POP字节码跃过至27日,该旋转False与剩余的顶部input_result以清除出去了POP_TOP,到然后返回剩余的False顶值作为结果.

True但是,如果结果是,该值由JUMP_IF_FALSE_OR_POP字节码弹出堆栈,并且在它的位置将10值加载到顶部,我们得到:

10    
input_result
Run Code Online (Sandbox Code Playgroud)

然后进行另一次比较并返回.

总之,基本上Python然后这样做:

stack_1 = stack_2 = input('Value:')
if 1 < stack_1:
    result = False
else:
    result = stack_2 < 10
Run Code Online (Sandbox Code Playgroud)

stack_*再次清除的值.

然后,堆栈保存未命名的中间结果以进行比较