jpp*_*jpp 7 python cpython ternary-operator python-3.x python-internals
我知道条件表达式(或三元运算符)在Python中是懒惰的.它们代表条件执行而不是条件选择.换句话说,以下只有一个a或被b评估:
c = a if condition else b
Run Code Online (Sandbox Code Playgroud)
我有兴趣知道它是如何在内部实现的.Python是否转换if为如下所示的语句,如果是,则转换发生在什么阶段?
if condition:
c = a
else:
c = b
Run Code Online (Sandbox Code Playgroud)
或者三元运算符实际上是一个独立且独立的表达式,完全单独定义?如果是这样,我可以访问条件表达式的CPython代码吗?
我看这解释以下什么三元运营商的做法,但他们没有明确如何得到实施:
编辑:您可以假设CPython参考实现.
Mar*_*ers 11
Python不需要转换任何东西,如果它想要的话就不能.
通过使用语言语法将条件表达式解析为抽象语法树,然后将其编译为字节码.您可以使用以下ast.parse()函数生成AST :
>>> import ast
>>> ast.parse('c = a if condition else b').body[0] # first statement in the tree
<_ast.Assign object at 0x10f05c550>
>>> ast.dump(ast.parse('c = a if condition else b').body[0])
"Assign(targets=[Name(id='c', ctx=Store())], value=IfExp(test=Name(id='condition', ctx=Load()), body=Name(id='a', ctx=Load()), orelse=Name(id='b', ctx=Load())))"
Run Code Online (Sandbox Code Playgroud)
注意ast.IfExp()AST中为节点生成的节点; 这是条件表达式的专用节点.它有test,body和orelse部分代表3个表达式组成状况,真假零件.这在ast模块Abstract Grammar部分中有记录:
Run Code Online (Sandbox Code Playgroud)expr = [...] | [...] | IfExp(expr test, expr body, expr orelse)
这表明每个元素的类型是另一个expr表达式节点.
然后将解析树编译为字节码,该字节码使用堆栈根据测试有条件地跳转到右侧部分; 我们可以将ast.parse()直接生成的AST传递给compile()函数,之后dis模块让我们看一下编译生成的字符码的人性化形式:
>>> import dis
>>> dis.dis(compile(ast.parse('c = a if condition else b'), '', 'exec'))
1 0 LOAD_NAME 0 (condition)
2 POP_JUMP_IF_FALSE 8
4 LOAD_NAME 1 (a)
6 JUMP_FORWARD 2 (to 10)
>> 8 LOAD_NAME 2 (b)
>> 10 STORE_NAME 3 (c)
12 LOAD_CONST 0 (None)
14 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
因此,如果条件为假,则解释器循环跳转到指令8,否则执行指令4和6,指令6跳到指令10(因此超过else表达式).最终结果是指令4或指令8将新结果放在堆栈顶部STORE_NAME以移动到变量.
一个if语句导致一个不同的AST节点,结果字节码碰巧非常相似,因为它也会使用跳转.但编译器将它们视为不同的语法片段,并且必须这样做.
表达式和语句是编程语言的两个非常不同的基本构建块.语句可以包含表达式,但表达式不能包含语句,只能包含其他表达式.并且表达式可以生成一个值(对于要使用的周围语法),但语句不能.因此,Python必须以非常不同的方式处理条件表达式,因为语法分析器知道何时期望语句以及何时允许表达式.如果将条件表达式转换为语句,则无法将此类表达式用作更大表达式的一部分!
因为if语句不是表达式,所以它不返回值(因为只有表达式可以生成值),因此生成的字节码不会在堆栈顶部生成一个值,供周围的Python代码使用(没有c = if condition : ...).if语句包含一个条件表达式和一个套件,它必须总是由更多的语句组成(有一个'表达式语句'可以让你只在一个语句中放置一个表达式,比如1 + 1在一行中),以及那些语句可以像执行任务一样"做东西"或从函数返回,但是他们所做的任何if事情都不会让某些东西返回.
这反映在if语句的AST节点定义中:
Run Code Online (Sandbox Code Playgroud)stmt = [...] | [...] | If(expr test, stmt* body, stmt* orelse)
因此,对于一个If节点,test它是唯一的表达式节点,body并且orelse都包含零个或多个语句.该orelse部分将任何elif ...:测试作为进一步的If()节点或任何其他类型的语句来形成无条件else:.使用零个或多个元素,您不能指望单个结果.
所以这不是CPython独有的,这适用于所有Python实现.Python 语法不是实现细节.