简体   繁体   English

如何在Python中实现三元运算符

[英]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: 换句话说,在以下中仅评估ab中的一个:

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. 它有testbodyorelse部分来表示构成条件,真假部分的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是唯一的表达式节点, bodyorelse都包含零个或多个语句。 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. 至于何时发生这种转换,我会假设在“编译”到字节码期间的某个时间。

What 什么

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_avalue_b

if condition then:
   do_a
else:
   do_b

is procedural, it does do_a or do_b . 是程序性的,它确实是do_ado_b

Note: Procedural is about doing, do this, then do that, or that. 注意:程序是关于做,做这个,然后做那个,或那个。 Functional is about value, is this or is that. 功能是关于价值,是这个还是那个。

How 怎么样

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM