三个操作数比较如何在Python下工作?

Ale*_*tko 3 python parsing comparison-operators python-internals

你能解释一下链式比较的句法分析树是什么样的吗?

据我所知,在大多数语言中,它基于运算符关联性构造节点,a < b < c因此将有一个布尔值作为左手或右手操作数.

但是在Python中,这样的表达式几乎等同于a < b and b < c(b仅评估一次).

这种转变的生成语法规则是什么?基本上,Python解释器在这种情况下构造解析树的作用是什么?

Mar*_*ers 8

比较语法是不是很有趣在这里,它只是让你追加多个比较器来操作:

comparison    ::=  or_expr ( comp_operator or_expr )*
comp_operator ::=  "<" | ">" | "==" | ">=" | "<=" | "!="
                   | "is" ["not"] | ["not"] "in"
Run Code Online (Sandbox Code Playgroud)

因此,让我们通过使用ast模块直接询问Python解析器(它只是要求Python编译器本身只返回抽象语法树):

>>> import ast
>>> ast.dump(ast.parse('a > b > c', mode='eval'))
"Expression(body=Compare(left=Name(id='a', ctx=Load()), ops=[Gt(), Gt()], comparators=[Name(id='b', ctx=Load()), Name(id='c', ctx=Load())]))"
Run Code Online (Sandbox Code Playgroud)

因此,有只是一个单一的 Compare节点,与多个运营商和比较:

Compare(
    left=Name(id='a'),
    ops=[Gt(), Gt()],
    comparators=[Name(id='b'), Name(id='c')])
Run Code Online (Sandbox Code Playgroud)

(我省略了Expressionctx部分).

这使得解释器可以根据需要评估比较器(例如,如果a < b为假,则不必考虑其余的比较器).

生成的字节码使用条件跳转来跳过剩余的比较:

>>> import dis
>>> dis.dis(compile('a > b > c', '', 'eval'))
  1           0 LOAD_NAME                0 (a)
              2 LOAD_NAME                1 (b)
              4 DUP_TOP
              6 ROT_THREE
              8 COMPARE_OP               4 (>)
             10 JUMP_IF_FALSE_OR_POP    18
             12 LOAD_NAME                2 (c)
             14 COMPARE_OP               4 (>)
             16 RETURN_VALUE
        >>   18 ROT_TWO
             20 POP_TOP
             22 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)