簡體   English   中英

如何在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代碼嗎?

我看了這說明三元運營商做什么下文,但它們都沒有明確它們是如何實現的:


編輯:您可以假設CPython參考實現。

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())))"

注意為賦值生成的AST中的ast.IfExp()節點; 這是條件表達式的專用節點。 它有testbodyorelse部分來表示構成條件,真假部分的3個表達式。 這在ast模塊Abstract Grammar部分中有記錄

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

這表明每個元素的類型是另一個expr表達式節點。

然后將解析樹編譯為字節碼,該字節碼使用堆棧根據測試有條件地跳轉到右側部分; 我們可以將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

因此,如果條件為假,則解釋器循環跳轉到指令8,否則執行指令4和6,指令6跳轉到指令10(因此超過else表達式)。 最終結果是指令4或指令8將新結果放在堆棧頂部,以便STORE_NAME移動到變量。

if語句導致不同的AST節點,並且生成的字節碼恰好非常相似,因為它也會使用跳轉。 但編譯器將它們視為不同的語法片段,並且必須這樣做。

表達式和語句是編程語言的兩個非常不同的基本構建塊 語句可以包含表達式,但表達式不能包含語句,只能包含其他表達式。 並且表達式可以生成一個值(對於要使用的周圍語法),但語句不能 因此,Python必須以非常不同的方式處理條件表達式,因為語法分析器知道何時期望語句以及何時允許表達式。 如果將條件表達式轉換為語句,則無法將此類表達式用作更大表達式的一部分!

因為if語句不是表達式 ,所以它不返回值(因為只有表達式可以產生值),因此生成的字節碼不會在堆棧頂部產生一個值,供周圍的Python代碼使用(沒有c = if condition : ... )。 if語句包含一個條件表達式和一個套件 ,它必須總是由更多的語句組成(有一個'表達式語句'可以讓你只在一個語句中放置一個表達式,例如1 + 1在一行上),這些陳述可以“做東西”,如作業或從函數返回,但if返回某些內容,他們所做的一切都不會做。

這反映在if語句的AST節點定義中:

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

因此對於If節點, test是唯一的表達式節點, bodyorelse都包含零個或多個語句。 orelse部分將保存任何elif ...:作為進一步的If()節點或任何其他類型的語句測試以形成無條件的else: . 使用零個或多個元素,您不能指望單個結果。

所以這不是CPython獨有的,這適用於所有Python實現。 Python 語法不是實現細節。

Python是否轉換為if語句,如下所示

幾乎。

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和一個額外的STORE_FAST由@ L3viathan指出。

我們也得到幾乎相同的執行時間(差別可以忽略不計):

from timeit import Timer

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

至於何時發生這種轉換,我會假設在“編譯”到字節碼期間的某個時間。

什么

如果你在問什么,那么為了最好地理解它,你需要理解功能和程序之間的區別。 一個可以轉換為另一個,但兩者都可以獨立查看,您不必將其中一個轉換為另一個來理解它們。

value_a if condition else value_b是否有效,並返回值value_avalue_b

if condition then:
   do_a
else:
   do_b

是程序性的,它確實是do_ado_b

注意:程序是關於做,做這個,然后做那個,或那個。 功能是關於價值,是這個還是那個。

怎么樣

如果您正在詢問如何,那么您將需要查看其中一個實現的源代碼。 請注意,只要行為正確,每個實現都不必以相同的方式執行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM