Python中的三元运算符是如何实现的

How is ternary operator implemented in Python

我知道条件表达式(或三元运算符)在 Python 中是惰性的。它们代表条件执行而不是条件选择。换句话说,只有 ab 中的一个被评估如下:

c = a if condition else b

我想知道的是如何这是内部实施的。 Python 是否转换为如下所示的 if 语句?如果是,此转换发生在哪个阶段?

if condition:
    c = a
else:
    c = b

或者三元运算符实际上是一个不同的独立表达式,完全单独定义?如果是这样,我可以访问条件表达式的 CPython 代码吗?

我看过以下解释什么三元运算符的作用,但none其中清楚如何 已实施:


编辑: 您可以假定 CPython 参考实现。

Python 不需要转换任何东西,如果需要也不能。

条件表达式使用language grammar into an abstract syntax tree, which in turn is then compiled to bytecode. You can produce the AST by using the ast.parse() function解析:

>>> 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() 节点;这是条件表达式的专用节点。它有 testbodyorelse 部分来表示构成条件、真和假部分的 3 个表达式。这记录在 ast module Abstract Grammar section:

expr = [...]
     | [...]
     | IfExp(expr test, expr body, expr orelse)

这表明每个元素的类型是另一个expr表达式节点。

然后将解析树编译为字节码,使用堆栈根据测试有条件地跳转到正确的部分;我们可以将 ast.parse() 生成的 AST 直接传递给 compile() function, after which the 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 语句 不是表达式 ,所以它 return 不是一个值 (因为只有表达式可以产生一个值),因此生成的字节码不会在堆栈顶部产生一个值供周围的 Python 代码使用(没有 c = if condition : ...)。 if 语句包含一个 条件表达式 和一个 套件 ,它必须总是由更多的语句组成(有一个'expression statement' 让你只在语句中放置一个表达式,例如 1 + 1 在一行中),并且这些语句可以 'do stuff' 像赋值或函数中的 return ,但他们所做的任何事情都不会 if return 有所作为。

这反映在 if 语句的 AST 节点定义中:

stmt =  [...]
      | [...]
      | If(expr test, stmt* body, stmt* orelse)

所以对于If节点,test是唯一的表达式节点,bodyorelse都由零个或多个 语句。 orelse 部分将包含任何 elif ...: 测试作为进一步的 If() 节点,或任何其他类型的语句以形成无条件 else:。对于零个或多个元素,您不能期待一个结果。

所以这不是 CPython 独有的,这适用于所有 Python 实现。 Python 语法 不是实现细节。

Does Python convert to an if statement as below

差不多。

import dis

def trenary():
    x = 'a' if 1 == 1 else 'b'

def normal_if():
    if 1 == 1:
        c = 'a'
    else:
        c = 'b'

print('trenary')
dis.dis(trenary)
print()
print('normal if')
dis.dis(normal_if)

这输出:

trenary
 68           0 LOAD_CONST               1 (1)
              2 LOAD_CONST               1 (1)
              4 COMPARE_OP               2 (==)
              6 POP_JUMP_IF_FALSE       12
              8 LOAD_CONST               2 ('a')
             10 JUMP_FORWARD             2 (to 14)
        >>   12 LOAD_CONST               3 ('b')
        >>   14 STORE_FAST               0 (x)
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE

normal if
 71           0 LOAD_CONST               1 (1)
              2 LOAD_CONST               1 (1)
              4 COMPARE_OP               2 (==)
              6 POP_JUMP_IF_FALSE       14

 72           8 LOAD_CONST               2 ('a')
             10 STORE_FAST               0 (c)
             12 JUMP_FORWARD             4 (to 18)

 74     >>   14 LOAD_CONST               3 ('b')
             16 STORE_FAST               0 (c)
        >>   18 LOAD_CONST               0 (None)
             20 RETURN_VALUE

这些看起来几乎相同,除了 JUMP_FORWARD 的位置和 @L3viathan 指出的额外 STORE_FAST

我们也得到了几乎相同的执行时间(差异可以忽略不计):

from timeit import Timer

print(min(Timer(trenary).repeat(5000, 5000)))
print(min(Timer(normal_if).repeat(5000, 5000)))
# 0.0006442809999998023
# 0.0006442799999994975

至于什么时候这种转换发生,我会假设在"compilation"到字节码的某个时间。

什么

如果您要问什么,那么为了最好地理解它,您需要了解功能性和程序性之间的区别。一个可以转换为另一个,但两者都可以独立查看,您无需将一个转换为另一个即可理解它们。

value_a if condition else value_b 是函数式的,returns 是一个值 value_avalue_b.

if condition then:
   do_a
else:
   do_b

是程序性的,它确实 do_ado_b

注意:程序就是做,做这个,然后做那个,或者那个。功能是关于价值的,是这个还是那个。

如何

如果您要问如何实现,那么您将需要查看其中一个实现的源代码。请注意,只要行为正确,每个实现不必以相同的方式进行。