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

时间:2015-02-26 23:20:29

标签: python comparison-operators python-internals

Python Doc for Comparisons说:

  

比较可以任意链接,例如,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"

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

if 1 < input("Val1:") < 10 < input("Val2:") < 20: print "woo!"

仅询问Val2 ,如果 Val1介于1&amp;之间10只打印“呜!” 如果 Val2也在10到20之间(证明它们可以“任意链接”)。这也是有道理的。

但我仍然很好奇这是如何在lexer / parser / compiler(或其他)级别实际实现/解释的。

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

x = input("Value:")
1 < x and x < 10: print "Is between 1 and 10"

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

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

1 个答案:

答案 0 :(得分:10)

您可以简单地让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重复最高值,在rotating the top three stack值到达之前:

1
input_result
input_result

COMPARE_OP使用<测试前两项,用结果替换前两项。

如果结果为FalseJUMP_IF_FALSE_OR_POP bytecode会跳转到27,这会将False旋转到剩余的input_result上,以清除{{1}然后返回剩余的POP_TOP最高值作为结果。

但是,如果结果为False,那么True字节码会在堆栈中弹出该值,并且JUMP_IF_FALSE_OR_POP值被加载到顶部,我们得到:

10

进行另一次比较并返回。

总之,基本上Python就是这样做的:

10    
input_result

再次清除stack_1 = stack_2 = input('Value:') if 1 < stack_1: result = False else: result = stack_2 < 10 值。

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