[英]How is ternary operator implemented in Python
I understand that conditional expressions (or ternary operators) are lazy in Python. 我知道条件表达式(或三元运算符)在Python中是懒惰的。 They represent conditional execution rather than conditional selection . 它们代表条件执行而不是条件选择 。 In other words, only one of a
or b
is evaluated in the following: 换句话说,在以下中仅评估a
或b
中的一个:
c = a if condition else b
What I'm interested to know is how this is implemented internally. 我有兴趣知道它是如何在内部实现的。 Does Python convert to an if
statement as below and, if so, at what stage does this conversion occur? Python是否转换为如下所示的if
语句,如果是,则转换发生在什么阶段?
if condition:
c = a
else:
c = b
Or is the ternary operator actually a distinct and separate expression, defined entirely separately? 或者三元运算符实际上是一个独立且独立的表达式,完全单独定义? If so, can I access the CPython code for conditional expressions? 如果是这样,我可以访问条件表达式的CPython代码吗?
I've looked at the following which explain what the ternary operator does, but none of them make clear how they are implemented: 我看了这说明三元运营商做什么下文,但它们都没有明确它们是如何实现的:
Edit: You can assume CPython reference implementation. 编辑:您可以假设CPython参考实现。
Python doesn't have to convert anything, and couldn't if it wanted to. Python不需要转换任何东西,如果它想要的话就不能。
The conditional expression is parsed by using the 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 : 您可以使用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())))"
Note the ast.IfExp()
node in the AST produced for the assignment; 注意为赋值生成的AST中的ast.IfExp()
节点; this is a dedicated node for conditional expressions. 这是条件表达式的专用节点。 It has test
, body
and orelse
parts to represent the 3 expressions that make up the condition, true and false parts. 它有test
, body
和orelse
部分来表示构成条件,真假部分的3个表达式。 This is documented in the ast
module Abstract Grammar section : 这在ast
模块Abstract Grammar部分中有记录 :
expr = [...] | [...] | IfExp(expr test, expr body, expr orelse)
This shows that the type of each element is another expr
expression node. 这表明每个元素的类型是另一个expr
表达式节点。
The parse tree is then compiled to bytecode that uses the stack to conditionally jump to the right section based on the test; 然后将解析树编译为字节码,该字节码使用堆栈根据测试有条件地跳转到右侧部分; we can pass the AST produced by ast.parse()
directly to the compile()
function , after which the dis
module lets us look at a human-friendly form of the bytecode produced by compilation: 我们可以将ast.parse()
生成的AST直接传递给compile()
函数 ,之后dis
模块让我们看一下compile()
生成的ast.parse()
码的人性友好形式:
>>> 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
So if the condition is false, the interpreter loop jumps forward to instruction 8, otherwise instructions 4 and 6 are executed, with instruction 6 jumping forward to instruction 10 (so past the else
expression). 因此,如果条件为假,则解释器循环跳转到指令8,否则执行指令4和6,指令6跳转到指令10(因此超过else
表达式)。 The end result is that either instruction 4 or instruction 8 puts a new result on the top of the stack for STORE_NAME
to move to a variable. 最终结果是指令4或指令8将新结果放在堆栈顶部,以便STORE_NAME
移动到变量。
An if
statement results in a different AST node, and the resulting bytecode happens to be very similar in that it too would use jumps. if
语句导致不同的AST节点,并且生成的字节码恰好非常相似,因为它也会使用跳转。 But the compiler treats them as distinct pieces of syntax, and it has to. 但编译器将它们视为不同的语法片段,并且必须这样做。
Expressions and statements are two very different fundamental building blocks of a programming language. 表达式和语句是编程语言的两个非常不同的基本构建块 。 Statements can contain expressions, but expressions can't contain statements, only other expressions. 语句可以包含表达式,但表达式不能包含语句,只能包含其他表达式。 And expressions can produce a value (for the surrounding syntax to use), but statements can't . 并且表达式可以生成一个值(对于要使用的周围语法),但语句不能 。 So Python has to treat conditional expressions very differently from statements, in that the grammar parser knows when to expect a statement and when an expression is allowed. 因此,Python必须以非常不同的方式处理条件表达式,因为语法分析器知道何时期望语句以及何时允许表达式。 If you transformed a conditional expression into a statement, you would not be able to ever use such an expression as part of a bigger expression! 如果将条件表达式转换为语句,则无法将此类表达式用作更大表达式的一部分!
Because an if
statement is not an expression , it doesn't return a value (as only expressions can produce a value), and so the resulting bytecode would not produce a value on the top of the stack to be used by the surrounding Python code (there is no c = if condition : ...
). 因为if
语句不是表达式 ,所以它不返回值(因为只有表达式可以产生值),因此生成的字节码不会在堆栈顶部产生一个值,供周围的Python代码使用(没有c = if condition : ...
)。 if
statements contain a condition expression , and a suite , which must always consist of more statements (there is such a thing as an 'expression statement' to let you put just an expression in a statement, such as 1 + 1
on a single line), and those statements can 'do stuff' like assignments or return from a function, but nothing they do would ever make if
return something. if
语句包含一个条件表达式和一个套件 ,它必须总是由更多的语句组成(有一个'表达式语句'可以让你只在一个语句中放置一个表达式,例如1 + 1
在一行上),这些陈述可以“做东西”,如作业或从函数返回,但if
返回某些内容,他们所做的一切都不会做。
This is reflected in the AST node definition for if
statements: 这反映在if
语句的AST节点定义中:
stmt = [...] | [...] | If(expr test, stmt* body, stmt* orelse)
So for an If
node, test
is the only expression node, and body
and orelse
both consist of zero or more statements. 因此对于If
节点, test
是唯一的表达式节点, body
和orelse
都包含零个或多个语句。 The orelse
part would hold any elif ...:
tests as further If()
nodes, or any other type of statement to form an unconditional else:
. orelse
部分将保存任何elif ...:
作为进一步的If()
节点或任何其他类型的语句测试以形成无条件的else:
. With zero-or-more elements, you can't expect a single result. 使用零个或多个元素,您不能指望单个结果。
So this isn't unique to CPython, this applies to all Python implementations. 所以这不是CPython独有的,这适用于所有Python实现。 The Python grammar is not an implementation detail. Python 语法不是实现细节。
Does Python convert to an if statement as below Python是否转换为if语句,如下所示
Almost. 几乎。
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)
This outputs: 这输出:
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
These look almost the same, except for the location of JUMP_FORWARD
and an additional STORE_FAST
as pointed out by @L3viathan. 这些看起来几乎一样,除了位置JUMP_FORWARD
和一个额外的STORE_FAST
由@ L3viathan指出。
We also get almost the same execution times (with a negligible difference): 我们也得到几乎相同的执行时间(差别可以忽略不计):
from timeit import Timer
print(min(Timer(trenary).repeat(5000, 5000)))
print(min(Timer(normal_if).repeat(5000, 5000)))
# 0.0006442809999998023
# 0.0006442799999994975
As to when this conversion happens, I'd assume sometime during "compilation" to bytecode. 至于何时发生这种转换,我会假设在“编译”到字节码期间的某个时间。
If you are asking what, then to best understand it, you need to understand the difference between functional and procedural. 如果你在问什么,那么为了最好地理解它,你需要理解功能和程序之间的区别。 One can be converted to the other, but both can be view independently, you don't have to translate one to the other to understand them. 一个可以转换为另一个,但两者都可以独立查看,您不必将其中一个转换为另一个来理解它们。
value_a if condition else value_b
is functional, and returns a value value_a
or value_b
. value_a if condition else value_b
是否有效,并返回值value_a
或value_b
。
if condition then:
do_a
else:
do_b
is procedural, it does do_a
or do_b
. 是程序性的,它确实是do_a
或do_b
。
Note: Procedural is about doing, do this, then do that, or that. 注意:程序是关于做,做这个,然后做那个,或那个。 Functional is about value, is this or is that. 功能是关于价值,是这个还是那个。
If you are asking how, then you will need to look at the source-code of one of the implementations. 如果您正在询问如何,那么您将需要查看其中一个实现的源代码。 Note that each implementation does not have to do it the same way, so long as behaviour is correct. 请注意,只要行为正确,每个实现都不必以相同的方式执行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.