为什么这个if命令没有语法错误,它的评估结果是什么?

9 浏览
0 Comments

为什么这个if命令没有语法错误,它的评估结果是什么?

Python文档中的比较部分指出:比较可以任意链接,例如,`x < y <= z`等同于`x < y and y <= z`,但是当`x < y`为假时,`y`只被评估一次(但在这两种情况下,`z`都不会被评估)。\n以下的stackoverflow问题/回答对这种用法有更多的解释:\n- [Python comparison operators chaining/grouping left to right?](https://stackoverflow.com/questions/25753474/python-comparison-operators-chaining-grouping-left-to-right)\n- [What does \"evaluated only once\" mean for chained comparisons in Python?](https://stackoverflow.com/questions/1664292/what-does-evaluated-only-once-mean-for-chained-comparisons-in-python),特别是[当前接受的答案](https://stackoverflow.com/questions/1664292/what-does-evaluated-only-once-mean-for-chained-comparisons-in-python/1664307#1664307)。\n因此,像下面这样的(假设的)例子:\n[apcode language="python"]

if 1 < input("Value:") < 10: 
    print "Is greater than 1 and less than 10"

[/apcode]\n只会询问一次输入。这是有道理的。而且这个例子:\n

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

\n只有在`Val1`在1到10之间,并且`Val2`也在10到20之间时才会询问`Val2`,并且只有在这种情况下才会打印出\"woo!\"(证明它们可以任意链接)。这也是有道理的。\n但我仍然好奇这在词法分析器/解析器/编译器(或其他什么级别)上是如何实际实现/解释的。\n上面的第一个示例是否基本上像这样实现:\n

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

\n其中`x`实际上只存在(并且实际上是无名的)用于这些比较吗?或者它是否以某种方式使比较运算符返回布尔结果和右操作数的评估(用于进一步的比较)之类的内容?\n将分析扩展到第二个示例,我认为它使用了类似于无名中间结果的东西(如果有术语,请告诉我),因为它在执行比较之前并不评估所有的操作数。

0
0 Comments

为什么这个if命令没有语法错误,并且它的结果是什么?

在Python中,可以使用dis模块让Python告诉你生成的字节码是什么:

>>> 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使用<测试前两个项目,用结果替换前两个项目。

如果结果是False,则JUMP_IF_FALSE_OR_POP字节码跳转到27,将顶部的False与剩余的input_result进行旋转以通过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_*值。

堆栈中保存着未命名的中间结果进行比较。

真棒,谢谢!我不知道有dis这个方法,这很酷。将此方法扩展到第二个例子中,它做了同样的事情(当然是有道理的)-它将之前的结果保留在堆栈上,将输入放到堆栈上,并进行下一个比较。太棒了!

我认为你对ROT_THREE操作的结果有误。它应该将堆栈变为[input_result, 1, input_result]。我会修正它,但我不知道你如何更正“Python实际上执行以下操作”这部分的内容。

0