Python 中的三个操作数比较在幕后如何工作?
How does three operands comparison work in Python under the hood?
能否解释一下链式比较的句法分析树是什么样的?
据我所知,在大多数语言中,它基于运算符关联性构造节点,因此在 a < b < c
中,将有一个布尔值作为左操作数或右操作数。
但在 Python 中,这样的表达式几乎等同于 a < b and b < c
(b
仅计算一次)。
这种转换的生成语法规则是什么?基本上,在这种情况下,Python 解释器如何构建解析树?
comparison grammar 在这里没什么意思,它只是让您将多个比较器附加到一个运算符:
comparison ::= or_expr ( comp_operator or_expr )*
comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="
| "is" ["not"] | ["not"] "in"
所以让我们通过使用 ast
module 直接询问 Python 解析器(它只要求 Python 编译器本身仅 return 抽象语法树):
>>> 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())]))"
所以只有一个单个 Compare
节点,有多个运算符和比较器:
Compare(
left=Name(id='a'),
ops=[Gt(), Gt()],
comparators=[Name(id='b'), Name(id='c')])
(我省略了 Expression
和 ctx
部分)。
这让解释器可以根据需要评估比较器(例如,如果 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
能否解释一下链式比较的句法分析树是什么样的?
据我所知,在大多数语言中,它基于运算符关联性构造节点,因此在 a < b < c
中,将有一个布尔值作为左操作数或右操作数。
但在 Python 中,这样的表达式几乎等同于 a < b and b < c
(b
仅计算一次)。
这种转换的生成语法规则是什么?基本上,在这种情况下,Python 解释器如何构建解析树?
comparison grammar 在这里没什么意思,它只是让您将多个比较器附加到一个运算符:
comparison ::= or_expr ( comp_operator or_expr )*
comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!="
| "is" ["not"] | ["not"] "in"
所以让我们通过使用 ast
module 直接询问 Python 解析器(它只要求 Python 编译器本身仅 return 抽象语法树):
>>> 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())]))"
所以只有一个单个 Compare
节点,有多个运算符和比较器:
Compare(
left=Name(id='a'),
ops=[Gt(), Gt()],
comparators=[Name(id='b'), Name(id='c')])
(我省略了 Expression
和 ctx
部分)。
这让解释器可以根据需要评估比较器(例如,如果 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