Python 不需要转换任何东西,如果它愿意也不能。
使用language grammar 将条件表达式解析为abstract syntax tree,然后将其编译为字节码。您可以使用 ast.parse() function 生成 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())))"
注意为分配生成的 AST 中的 ast.IfExp() 节点;这是条件表达式的专用节点。它有test、body 和orelse 部分来表示构成条件的3 个表达式,true 和false 部分。这记录在ast module Abstract Grammar section:
expr = [...]
| [...]
| IfExp(expr test, expr body, expr orelse)
这说明每个元素的类型是另一个expr表达式节点。
然后解析树被编译成字节码,使用栈根据测试条件跳转到正确的部分;我们可以将ast.parse()产生的AST直接传递给compile() function,然后dis module让我们看一下编译产生的对人类友好的字节码形式:
>>> 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
因此,如果条件为假,解释器循环向前跳转到指令 8,否则执行指令 4 和 6,指令 6 向前跳转到指令 10(因此通过 else 表达式)。最终结果是指令 4 或指令 8 将新结果放在堆栈顶部,以便 STORE_NAME 移动到变量中。
if 语句会产生不同的 AST 节点,而产生的字节码 恰好 非常相似,因为它也会使用跳转。但是编译器将它们视为不同的语法片段,并且它必须这样做。
表达式和语句是编程语言的两个非常不同的基本构建块。语句可以包含表达式,但表达式不能包含语句,只能包含其他表达式。表达式可以产生一个值(供周围的语法使用),但语句不能。因此,Python 必须以非常不同于语句的方式处理条件表达式,因为语法解析器知道何时期望语句以及何时允许表达式。如果将条件表达式转换为语句,则永远无法将这样的表达式用作更大表达式的一部分!
因为if 语句不是表达式,它不返回值(因为只有表达式才能产生值),因此生成的字节码不会在堆栈顶部生成值以供周围的 Python 代码使用(没有c = if condition : ...)。 if 语句包含一个 条件表达式 和一个 suite,它必须始终包含更多语句(有一个“表达式语句”之类的东西可以让你放只是语句中的一个表达式,例如单行上的1 + 1),这些语句可以“做一些事情”,比如赋值或从函数返回,但它们所做的任何事情都不会让if 返回一些东西。
这反映在 if 语句的 AST 节点定义中:
stmt = [...]
| [...]
| If(expr test, stmt* body, stmt* orelse)
所以对于If 节点,test 是唯一的表达式节点,body 和orelse 都由零个或多个 语句组成。 orelse 部分将持有任何elif ...: 测试作为进一步的If() 节点,或任何其他类型的语句以形成无条件的else:。对于零个或多个元素,您不能期望一个结果。
所以这不是 CPython 独有的,它适用于所有 Python 实现。 Python 语法 不是实现细节。