小编典典

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

python

用于比较Python文档说:

可以任意链接比较,例如x < y <= z与等效x < y and y <= z,不同之处在于y比较仅被评估一次(但在两种情况下zx < 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那些比较中真正存在的地方(实际上实际上是未命名的)?还是以某种方式使比较运算符同时返回布尔结果和正确操作数的评估(将用于进一步比较)还是类似的东西?

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


阅读 213

收藏
2020-12-20

共1个答案

小编典典

您可以简单地让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,FalseJUMP_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_*值再次清除。

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

2020-12-20