8. 復(fù)合語句?

復(fù)合語句是包含其它語句(語句組)的語句;它們會(huì)以某種方式影響或控制所包含其它語句的執(zhí)行。 通常,復(fù)合語句會(huì)跨越多行,雖然在某些簡(jiǎn)單形式下整個(gè)復(fù)合語句也可能包含于一行之內(nèi)。

if, whilefor 語句用來實(shí)現(xiàn)傳統(tǒng)的控制流程構(gòu)造。 try 語句為一組語句指定異常處理和/和清理代碼,而 with 語句允許在一個(gè)代碼塊周圍執(zhí)行初始化和終結(jié)化代碼。 函數(shù)和類定義在語法上也屬于復(fù)合語句。

一條復(fù)合語句由一個(gè)或多個(gè)‘子句’組成。 一個(gè)子句則包含一個(gè)句頭和一個(gè)‘句體’。 特定復(fù)合語句的子句頭都處于相同的縮進(jìn)層級(jí)。 每個(gè)子句頭以一個(gè)作為唯一標(biāo)識(shí)的關(guān)鍵字開始并以一個(gè)冒號(hào)結(jié)束。 子句體是由一個(gè)子句控制的一組語句。 子句體可以是在子句頭的冒號(hào)之后與其同處一行的一條或由分號(hào)分隔的多條簡(jiǎn)單語句,或者也可以是在其之后縮進(jìn)的一行或多行語句。 只有后一種形式的子句體才能包含嵌套的復(fù)合語句;以下形式是不合法的,這主要是因?yàn)闊o法分清某個(gè)后續(xù)的 else 子句應(yīng)該屬于哪個(gè) if 子句:

if test1: if test2: print(x)

還要注意的是在這種情形下分號(hào)的綁定比冒號(hào)更緊密,因此在以下示例中,所有 print() 調(diào)用或者都不執(zhí)行,或者都執(zhí)行:

if x < y < z: print(x); print(y); print(z)

總結(jié):

compound_stmt ::=  if_stmt
                   | while_stmt
                   | for_stmt
                   | try_stmt
                   | with_stmt
                   | match_stmt
                   | funcdef
                   | classdef
                   | async_with_stmt
                   | async_for_stmt
                   | async_funcdef
suite         ::=  stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT
statement     ::=  stmt_list NEWLINE | compound_stmt
stmt_list     ::=  simple_stmt (";" simple_stmt)* [";"]

請(qǐng)注意語句總是以 NEWLINE 結(jié)束,之后可能跟隨一個(gè) DEDENT。 還要注意可選的后續(xù)子句總是以一個(gè)不能作為語句開頭的關(guān)鍵字作為開頭,因此不會(huì)產(chǎn)生歧義(‘懸空的 else’問題在 Python 中是通過要求嵌套的 if 語句必須縮進(jìn)來解決的)。

為了保證清晰,以下各節(jié)中語法規(guī)則采用將每個(gè)子句都放在單獨(dú)行中的格式。

8.1. if 語句?

if 語句用于有條件的執(zhí)行:

if_stmt ::=  "if" assignment_expression ":" suite
             ("elif" assignment_expression ":" suite)*
             ["else" ":" suite]

它通過對(duì)表達(dá)式逐個(gè)求值直至找到一個(gè)真值(請(qǐng)參閱 布爾運(yùn)算 了解真值與假值的定義)在子句體中選擇唯一匹配的一個(gè);然后執(zhí)行該子句體(而且 if 語句的其他部分不會(huì)被執(zhí)行或求值)。 如果所有表達(dá)式均為假值,則如果 else 子句體如果存在就會(huì)被執(zhí)行。

8.2. while 語句?

while 語句用于在表達(dá)式保持為真的情況下重復(fù)地執(zhí)行:

while_stmt ::=  "while" assignment_expression ":" suite
                ["else" ":" suite]

這將重復(fù)地檢驗(yàn)表達(dá)式,并且如果其值為真就執(zhí)行第一個(gè)子句體;如果表達(dá)式值為假(這可能在第一次檢驗(yàn)時(shí)就發(fā)生)則如果 else 子句體存在就會(huì)被執(zhí)行并終止循環(huán)。

第一個(gè)子句體中的 break 語句在執(zhí)行時(shí)將終止循環(huán)且不執(zhí)行 else 子句體。 第一個(gè)子句體中的 continue 語句在執(zhí)行時(shí)將跳過子句體中的剩余部分并返回檢驗(yàn)表達(dá)式。

8.3. for 語句?

for 語句用于對(duì)序列(例如字符串、元組或列表)或其他可迭代對(duì)象中的元素進(jìn)行迭代:

for_stmt ::=  "for" target_list "in" starred_list ":" suite
              ["else" ":" suite]

The starred_list expression is evaluated once; it should yield an iterable object. An iterator is created for that iterable. The first item provided by the iterator is then assigned to the target list using the standard rules for assignments (see 賦值語句), and the suite is executed. This repeats for each item provided by the iterator. When the iterator is exhausted, the suite in the else clause, if present, is executed, and the loop terminates.

第一個(gè)子句體中的 break 語句在執(zhí)行時(shí)將終止循環(huán)且不執(zhí)行 else 子句體。 第一個(gè)子句體中的 continue 語句在執(zhí)行時(shí)將跳過子句體中的剩余部分并轉(zhuǎn)往下一項(xiàng)繼續(xù)執(zhí)行,或者在沒有下一項(xiàng)時(shí)轉(zhuǎn)往 else 子句執(zhí)行。

for 循環(huán)會(huì)對(duì)目標(biāo)列表中的變量進(jìn)行賦值。 這將覆蓋之前對(duì)這些變量的所有賦值,包括在 for 循環(huán)體中的賦值:

for i in range(10):
    print(i)
    i = 5             # this will not affect the for-loop
                      # because i will be overwritten with the next
                      # index in the range

目標(biāo)列表中的名稱在循環(huán)結(jié)束時(shí)不會(huì)被刪除,但如果序列為空,則它們根本不會(huì)被循環(huán)所賦值。 提示:內(nèi)置函數(shù) range() 會(huì)返回一個(gè)可迭代的整數(shù)序列,適用于模擬 Pascal 中的 for i := a to b do 這種效果;例如 list(range(3)) 會(huì)返回列表 [0, 1, 2]

在 3.11 版更改: Starred elements are now allowed in the expression list.

8.4. try 語句?

try 語句可為一組語句指定異常處理器和/或清理代碼:

try_stmt  ::=  try1_stmt | try2_stmt | try3_stmt
try1_stmt ::=  "try" ":" suite
               ("except" [expression ["as" identifier]] ":" suite)+
               ["else" ":" suite]
               ["finally" ":" suite]
try2_stmt ::=  "try" ":" suite
               ("except" "*" expression ["as" identifier] ":" suite)+
               ["else" ":" suite]
               ["finally" ":" suite]
try3_stmt ::=  "try" ":" suite
               "finally" ":" suite

The except clause(s) specify one or more exception handlers. When no exception occurs in the try clause, no exception handler is executed. When an exception occurs in the try suite, a search for an exception handler is started. This search inspects the except clauses in turn until one is found that matches the exception. An expression-less except clause, if present, must be last; it matches any exception. For an except clause with an expression, that expression is evaluated, and the clause matches the exception if the resulting object is "compatible" with the exception. An object is compatible with an exception if the object is the class or a non-virtual base class of the exception object, or a tuple containing an item that is the class or a non-virtual base class of the exception object.

如果沒有 except 子句與異常相匹配,則會(huì)在周邊代碼和發(fā)起調(diào)用棧上繼續(xù)搜索異常處理器。 1

如果在對(duì) except 子句頭中的表達(dá)式求值時(shí)引發(fā)了異常,則原來對(duì)處理器的搜索會(huì)被取消,并在周邊代碼和調(diào)用棧上啟動(dòng)對(duì)新異常的搜索(它會(huì)被視作是整個(gè) try 語句所引發(fā)的異常)。

當(dāng)找到一個(gè)匹配的 except 子句時(shí),該異常將被賦值給該 except 子句在 as 關(guān)鍵字之后指定的目標(biāo),如果存在此關(guān)鍵字的話,并且該 except 子句體將被執(zhí)行。 所有 except 子句都必須有可執(zhí)行的子句體。 當(dāng)?shù)竭_(dá)子句體的末尾時(shí),通常會(huì)轉(zhuǎn)向整個(gè) try 語句之后繼續(xù)執(zhí)行。 (這意味著如果對(duì)于同一異常存在有嵌套的兩個(gè)處理器,而異常發(fā)生于內(nèi)層處理器的 try 子句中,則外層處理器將不會(huì)處理該異常。)

當(dāng)使用 as 將目標(biāo)賦值為一個(gè)異常時(shí),它將在 except 子句結(jié)束時(shí)被清除。 這就相當(dāng)于

except E as N:
    foo

被轉(zhuǎn)寫為

except E as N:
    try:
        foo
    finally:
        del N

這意味著異常必須賦值給一個(gè)不同的名稱才能在 except 子句之后引用它。 異常會(huì)被清除是因?yàn)樵诟郊恿嘶厮菪畔⒌那闆r下,它們會(huì)形成堆棧幀的循環(huán)引用,使得所有局部變量保持存活直到發(fā)生下一次垃圾回收。

在一個(gè) except 子句體被執(zhí)行之前,有關(guān)異常的詳細(xì)信息存放在 sys 模塊中,可通過 sys.exc_info() 來訪問。 sys.exc_info() 返回一個(gè) 3 元組,由異常類、異常實(shí)例和回溯對(duì)象組成(參見 標(biāo)準(zhǔn)類型層級(jí)結(jié)構(gòu) 一節(jié)),用于在程序中標(biāo)識(shí)異常發(fā)生點(diǎn)。 當(dāng)從處理異常的代碼返回時(shí),通過 sys.exc_info() 訪問的異常詳細(xì)信息會(huì)恢復(fù)到之前的值:

>>>
>>> print(sys.exc_info())
(None, None, None)
>>> try:
...     raise TypeError
... except:
...     print(sys.exc_info())
...     try:
...          raise ValueError
...     except:
...         print(sys.exc_info())
...     print(sys.exc_info())
...
(<class 'TypeError'>, TypeError(), <traceback object at 0x10efad080>)
(<class 'ValueError'>, ValueError(), <traceback object at 0x10efad040>)
(<class 'TypeError'>, TypeError(), <traceback object at 0x10efad080>)
>>> print(sys.exc_info())
(None, None, None)

The except* clause(s) are used for handling ExceptionGroups. The exception type for matching is interpreted as in the case of except, but in the case of exception groups we can have partial matches when the type matches some of the exceptions in the group. This means that multiple except* clauses can execute, each handling part of the exception group. Each clause executes once and handles an exception group of all matching exceptions. Each exception in the group is handled by at most one except* clause, the first that matches it.

>>>
>>> try:
...     raise ExceptionGroup("eg",
...         [ValueError(1), TypeError(2), OSError(3), OSError(4)])
... except* TypeError as e:
...     print(f'caught {type(e)} with nested {e.exceptions}')
... except* OSError as e:
...     print(f'caught {type(e)} with nested {e.exceptions}')
...
caught <class 'ExceptionGroup'> with nested (TypeError(2),)
caught <class 'ExceptionGroup'> with nested (OSError(3), OSError(4))
  + Exception Group Traceback (most recent call last):
  |   File "<stdin>", line 2, in <module>
  | ExceptionGroup: eg
  +-+---------------- 1 ----------------
    | ValueError: 1
    +------------------------------------
>>>

Any remaining exceptions that were not handled by any except* clause
are re-raised at the end, combined into an exception group along with
all exceptions that were raised from within except* clauses.

An except* clause must have a matching type, and this type cannot be a
subclass of :exc:`BaseExceptionGroup`. It is not possible to mix except
and except* in the same :keyword:`try`. :keyword:`break`,
:keyword:`continue` and :keyword:`return` cannot appear in an except*
clause.

如果控制流離開 try 子句體時(shí)沒有引發(fā)異常,并且沒有執(zhí)行 return, continuebreak 語句,可選的 else 子句將被執(zhí)行。 else 語句中的異常不會(huì)由之前的 except 子句處理。

如果存在 finally,它將指定一個(gè)‘清理’處理程序。 try 子句會(huì)被執(zhí)行,包括任何 exceptelse 子句。 如果在這些子句中發(fā)生任何未處理的異常,該異常會(huì)被臨時(shí)保存。 finally 子句將被執(zhí)行。 如果存在被保存的異常,它會(huì)在 finally 子句的末尾被重新引發(fā)。 如果 finally 子句引發(fā)了另一個(gè)異常,被保存的異常會(huì)被設(shè)為新異常的上下文。 如果 finally 子句執(zhí)行了 return, breakcontinue 語句,則被保存的異常會(huì)被丟棄:

>>>
>>> def f():
...     try:
...         1/0
...     finally:
...         return 42
...
>>> f()
42

finally 子句執(zhí)行期間,程序不能獲取異常信息。

當(dāng) return, breakcontinue 語句在一個(gè) try...finally 語句的 try 子語句體中被執(zhí)行時(shí),finally 子語句也會(huì)‘在離開時(shí)’被執(zhí)行。

函數(shù)的返回值是由最后被執(zhí)行的 return 語句所決定的。 由于 finally 子句總是被執(zhí)行,因此在 finally 子句中被執(zhí)行的 return 語句總是最后被執(zhí)行的:

>>>
>>> def foo():
...     try:
...         return 'try'
...     finally:
...         return 'finally'
...
>>> foo()
'finally'

有關(guān)異常的更多信息可以在 異常 一節(jié)找到,有關(guān)使用 raise 語句生成異常的信息可以在 raise 語句 一節(jié)找到。

在 3.8 版更改: 在 Python 3.8 之前,continue 語句不允許在 finally 子句中使用,這是因?yàn)榫唧w實(shí)現(xiàn)存在一個(gè)問題。

8.5. with 語句?

with 語句用于包裝帶有使用上下文管理器 (參見 with 語句上下文管理器 一節(jié)) 定義的方法的代碼塊的執(zhí)行。 這允許對(duì)普通的 try...except...finally 使用模式進(jìn)行封裝以方便地重用。

with_stmt          ::=  "with" ( "(" with_stmt_contents ","? ")" | with_stmt_contents ) ":" suite
with_stmt_contents ::=  with_item ("," with_item)*
with_item          ::=  expression ["as" target]

帶有一個(gè)“項(xiàng)目”的 with 語句的執(zhí)行過程如下:

  1. The context expression (the expression given in the with_item) is evaluated to obtain a context manager.

  2. 載入上下文管理器的 __enter__() 以便后續(xù)使用。

  3. 載入上下文管理器的 __exit__() 以便后續(xù)使用。

  4. 發(fā)起調(diào)用上下文管理器的 __enter__() 方法。

  5. 如果 with 語句中包含一個(gè)目標(biāo),來自 __enter__() 的返回值將被賦值給它。

    備注

    with 語句會(huì)保證如果 __enter__() 方法返回時(shí)未發(fā)生錯(cuò)誤,則 __exit__() 將總是被調(diào)用。 因此,如果在對(duì)目標(biāo)列表賦值期間發(fā)生錯(cuò)誤,則會(huì)將其視為在語句體內(nèi)部發(fā)生的錯(cuò)誤。 參見下面的第 6 步。

  6. 執(zhí)行語句體。

  7. 發(fā)起調(diào)用上下文管理器的 __exit__() 方法。 如果語句體的退出是由異常導(dǎo)致的,則其類型、值和回溯信息將被作為參數(shù)傳遞給 __exit__()。 否則的話,將提供三個(gè) None 參數(shù)。

    如果語句體的退出是由異常導(dǎo)致的,并且來自 __exit__() 方法的返回值為假,則該異常會(huì)被重新引發(fā)。 如果返回值為真,則該異常會(huì)被抑制,并會(huì)繼續(xù)執(zhí)行 with 語句之后的語句。

    如果語句體由于異常以外的任何原因退出,則來自 __exit__() 的返回值會(huì)被忽略,并會(huì)在該類退出正常的發(fā)生位置繼續(xù)執(zhí)行。

以下代碼:

with EXPRESSION as TARGET:
    SUITE

在語義上等價(jià)于:

manager = (EXPRESSION)
enter = type(manager).__enter__
exit = type(manager).__exit__
value = enter(manager)
hit_except = False

try:
    TARGET = value
    SUITE
except:
    hit_except = True
    if not exit(manager, *sys.exc_info()):
        raise
finally:
    if not hit_except:
        exit(manager, None, None, None)

如果有多個(gè)項(xiàng)目,則會(huì)視作存在多個(gè) with 語句嵌套來處理多個(gè)上下文管理器:

with A() as a, B() as b:
    SUITE

在語義上等價(jià)于:

with A() as a:
    with B() as b:
        SUITE

也可以用圓括號(hào)包圍的多行形式的多項(xiàng)目上下文管理器。例如:

with (
    A() as a,
    B() as b,
):
    SUITE

在 3.1 版更改: 支持多個(gè)上下文表達(dá)式。

在 3.10 版更改: Support for using grouping parentheses to break the statement in multiple lines.

參見

PEP 343 - "with" 語句

Python with 語句的規(guī)范描述、背景和示例。

8.6. match 語句?

3.10 新版功能.

匹配語句用于進(jìn)行模式匹配。語法如下:

match_stmt   ::=  'match' subject_expr ":" NEWLINE INDENT case_block+ DEDENT
subject_expr ::=  star_named_expression "," star_named_expressions?
                  | named_expression
case_block   ::=  'case' patterns [guard] ":" block

備注

本節(jié)使用單引號(hào)來表示 軟關(guān)鍵字。

模式匹配接受一個(gè)模式作為輸入(跟在 case 后),一個(gè)主詞值(跟在 match 后)。該模式(可能包含子模式)與主題值進(jìn)行匹配。輸出是:

  • 匹配成功或失?。ㄒ脖环Q為模式成功或失?。?。

  • 可能將匹配的值綁定到一個(gè)名字上。 這方面的先決條件將在下面進(jìn)一步討論。

關(guān)鍵字 matchcasesoft keywords

參見

  • PEP 634 —— 結(jié)構(gòu)化模式匹配:規(guī)范

  • PEP 636 —— 結(jié)構(gòu)化模式匹配:教程

8.6.1. 概述?

匹配語句邏輯流程的概述如下:

  1. 對(duì)主詞表達(dá)式 subject_expr 求值后將結(jié)果作為匹配用的結(jié)果主詞值。 如果主詞表達(dá)式包含逗號(hào),則使用 the standard rules 構(gòu)建一個(gè)元組。

  2. 主詞值將依次與 case_block 中的每個(gè)模式進(jìn)行匹配。匹配成功或失敗的具體規(guī)則在下面描述。匹配嘗試也可以與模式中的一些或所有的獨(dú)立名稱綁定。準(zhǔn)確的模式綁定規(guī)則因模式類型而異,具體規(guī)定見下文。成功的模式匹配過程中產(chǎn)生的名稱綁定將超越所執(zhí)行的塊的范圍,可以在匹配語句之后使用。

    備注

    在模式匹配失敗時(shí),一些子模式可能會(huì)成功。 不要依賴于失敗匹配進(jìn)行的綁定。 反過來說,不要認(rèn)為變量在匹配失敗后保持不變。 確切的行為取決于實(shí)現(xiàn),可能會(huì)有所不同。 這是一個(gè)有意的決定,允許不同的實(shí)現(xiàn)添加優(yōu)化。

  3. 如果該模式匹配成功,并且完成了對(duì)相應(yīng)的約束項(xiàng)(如果存在)的求值。在這種情況下,保證完成所有的名稱綁定。

    • If the guard evaluates as true or is missing, the block inside case_block is executed.

    • 否則,將按照上述方法嘗試下一個(gè) case_block

    • 如果沒有進(jìn)一步的 case 塊,匹配語句終止。

備注

用戶一般不應(yīng)依賴正在求值的模式。 根據(jù)不同的實(shí)現(xiàn)方式,解釋器可能會(huì)緩存數(shù)值或使用其他優(yōu)化方法來避免重復(fù)求值。

匹配語句示例:

>>>
>>> flag = False
>>> match (100, 200):
...    case (100, 300):  # Mismatch: 200 != 300
...        print('Case 1')
...    case (100, 200) if flag:  # Successful match, but guard fails
...        print('Case 2')
...    case (100, y):  # Matches and binds y to 200
...        print(f'Case 3, y: {y}')
...    case _:  # Pattern not attempted
...        print('Case 4, I match anything!')
...
Case 3, y: 200

在這個(gè)示例中,if flag 是約束項(xiàng)。請(qǐng)閱讀下一節(jié)以了解更多相關(guān)內(nèi)容。

8.6.2. 約束項(xiàng)?

guard ::=  "if" named_expression

guard (它是 case 的一部分) 必須成立才能讓 case 語句塊中的代碼被執(zhí)行。 它所采用的形式為: if 之后跟一個(gè)表達(dá)式。

擁有 guardcase 塊的邏輯流程如下:

  1. 檢查 case 塊中的模式是否匹配成功。如果該模式匹配失敗,則不對(duì) guard 進(jìn)行求值,檢查下一個(gè) case 塊。

  2. 如果該模式匹配成功,對(duì) guard 求值。

    • If the guard condition evaluates as true, the case block is selected.

    • If the guard condition evaluates as false, the case block is not selected.

    • 如果在對(duì) guard 求值過程中引發(fā)了異常,則異常將被拋出。

允許約束項(xiàng)產(chǎn)生副作用,因?yàn)樗麄兪潜磉_(dá)式。約束項(xiàng)求值必須從第一個(gè) case 塊到最后一個(gè) case 塊依次逐個(gè)進(jìn)行,模式匹配失敗的 case 塊將被跳過。(也就是說,約束項(xiàng)求值必須按順序進(jìn)行。)一旦選用了一個(gè) case 塊,約束項(xiàng)求值必須由此終止。

8.6.3. 必定匹配的 case 塊?

必定匹配的 case 塊是能匹配所有情況的 case 塊。一個(gè)匹配語句最多可以有一個(gè)必定匹配的 case 塊,而且必須是最后一個(gè)。

如果一個(gè) case 塊沒有約束項(xiàng),并且其模式是必定匹配的,那么它就被認(rèn)為是必定匹配的。 如果我們可以僅從語法上證明一個(gè)模式總是能匹配成功,那么這個(gè)模式就被認(rèn)為是必定匹配的。 只有以下模式是必定匹配的:

8.6.4. 模式?

備注

本節(jié)使用了超出標(biāo)準(zhǔn) EBNF 的語法符號(hào)。

  • 符號(hào) SEP.RULE+RULE (SEP RULE)* 的簡(jiǎn)寫

  • 符號(hào) !RULE 是前向否定斷言的簡(jiǎn)寫

patterns 的頂層語法是:

patterns       ::=  open_sequence_pattern | pattern
pattern        ::=  as_pattern | or_pattern
closed_pattern ::=  | literal_pattern
                    | capture_pattern
                    | wildcard_pattern
                    | value_pattern
                    | group_pattern
                    | sequence_pattern
                    | mapping_pattern
                    | class_pattern

下面的描述將包括一個(gè)“簡(jiǎn)而言之”以描述模式的作用,便于說明問題(感謝 Raymond Hettinger 提供的一份文件,大部分的描述受其啟發(fā))。請(qǐng)注意,這些描述純粹是為了說明問題,可能不 反映底層的實(shí)現(xiàn)。此外,它們并沒有涵蓋所有有效的形式。

8.6.4.1. 或模式?

或模式是由豎杠 | 分隔的兩個(gè)或更多的模式。語法:

or_pattern ::=  "|".closed_pattern+

只有最后的子模式可以是 必定匹配的,且每個(gè)子模式必須綁定相同的名字集以避免歧義。

或模式將主詞值依次與其每個(gè)子模式嘗試匹配,直到有一個(gè)匹配成功,然后該或模式被視作匹配成功。 否則,如果沒有任何子模式匹配成功,則或模式匹配失敗。

簡(jiǎn)而言之,P1 | P2 | ... 會(huì)首先嘗試匹配 P1 ,如果失敗將接著嘗試匹配 P2 ,如果出現(xiàn)成功的匹配則立即結(jié)束且模式匹配成功,否則模式匹配失敗。

8.6.4.2. AS 模式?

AS 模式將關(guān)鍵字 as 左側(cè)的或模式與主詞值進(jìn)行匹配。語法:

as_pattern ::=  or_pattern "as" capture_pattern

如果或模式匹配失敗,AS 模式也匹配失敗。 否則,AS 模式將主詞與關(guān)鍵字 as 右邊的名字綁定且匹配陳成功。 capture_pattern 不能是 _ 。

簡(jiǎn)而言之, P as NAME 將與 P 匹配,成功后將設(shè)置 NAME = <subject>

8.6.4.3. 字面值模式?

字面值模式對(duì)應(yīng) Python 中的大多數(shù) 字面值。 語法為:

literal_pattern ::=  signed_number
                     | signed_number "+" NUMBER
                     | signed_number "-" NUMBER
                     | strings
                     | "None"
                     | "True"
                     | "False"
                     | signed_number: NUMBER | "-" NUMBER

規(guī)則 strings 和標(biāo)記 NUMBER 是在 standard Python grammar 中定義的。支持三引號(hào)的字符串。不支持原始字符串和字節(jié)字符串。也不支持 格式字符串字面值 。

signed_number '+' NUMBERsigned_number '-' NUMBER 形式是用于表示 復(fù)數(shù);它們要求左邊是一個(gè)實(shí)數(shù)而右邊是一個(gè)虛數(shù)。 例如 3 + 4j。

簡(jiǎn)而言之, LITERAL 只會(huì)在 <subject> == LITERAL 時(shí)匹配成功。對(duì)于單例 None 、 TrueFalse ,會(huì)使用 is 運(yùn)算符。

8.6.4.4. 捕獲模式?

捕獲模式將主詞值與一個(gè)名稱綁定。語法:

capture_pattern ::=  !'_' NAME

A single underscore _ is not a capture pattern (this is what !'_' expresses). It is instead treated as a wildcard_pattern.

在給定的模式中,一個(gè)名字只能被綁定一次。例如 case x, x: ... 時(shí)無效的,但 case [x] | x: ... 是被允許的。

捕獲模式總是能匹配成功。綁定遵循 PEP 572 中賦值表達(dá)式運(yùn)算符設(shè)立的作用域規(guī)則;名字在最接近的包含函數(shù)作用域內(nèi)成為一個(gè)局部變量,除非有適用的 globalnonlocal 語句。

簡(jiǎn)而言之, NAME 總是會(huì)匹配成功且將設(shè)置 NAME = <subject> 。

8.6.4.5. 通配符模式?

通配符模式總是會(huì)匹配成功(匹配任何內(nèi)容)并且不綁定任何名稱。語法:

wildcard_pattern ::=  '_'

在且僅在任何模式中 _ 是一個(gè) 軟關(guān)鍵字。 通常情況下它是一個(gè)標(biāo)識(shí)符,即使是在 match 的目標(biāo)表達(dá)式、guardcase 代碼塊中也是如此。

簡(jiǎn)而言之,_ 總是會(huì)匹配成功。

8.6.4.6. 值模式?

值模式代表 Python 中具有名稱的值。語法:

value_pattern ::=  attr
attr          ::=  name_or_attr "." NAME
name_or_attr  ::=  attr | NAME

模式中帶點(diǎn)的名稱會(huì)使用標(biāo)準(zhǔn)的 Python 名稱解析規(guī)則 來查找。 如果找到的值與目標(biāo)值比較結(jié)果相等則模式匹配成功(使用 == 相等運(yùn)算符)。

簡(jiǎn)而言之, NAME1.NAME2 僅在 <subject> == NAME1.NAME2 時(shí)匹配成功。

備注

如果相同的值在同一個(gè)匹配語句中出現(xiàn)多次,解釋器可能會(huì)緩存找到的第一個(gè)值并重新使用它,而不是重復(fù)查找。 這種緩存與特定匹配語句的執(zhí)行嚴(yán)格掛鉤。

8.6.4.7. 組模式?

組模式允許用戶在模式周圍添加括號(hào),以強(qiáng)調(diào)預(yù)期的分組。 除此之外,它沒有額外的語法。語法:

group_pattern ::=  "(" pattern ")"

簡(jiǎn)單來說 (P) 具有與 P 相同的效果。

8.6.4.8. 序列模式?

一個(gè)序列模式包含數(shù)個(gè)將與序列元素進(jìn)行匹配的子模式。其語法類似于列表或元組的解包。

sequence_pattern       ::=  "[" [maybe_sequence_pattern] "]"
                            | "(" [open_sequence_pattern] ")"
open_sequence_pattern  ::=  maybe_star_pattern "," [maybe_sequence_pattern]
maybe_sequence_pattern ::=  ",".maybe_star_pattern+ ","?
maybe_star_pattern     ::=  star_pattern | pattern
star_pattern           ::=  "*" (capture_pattern | wildcard_pattern)

序列模式中使用圓括號(hào)或方括號(hào)沒有區(qū)別(例如 (...)[...] )。

備注

用圓括號(hào)括起來且沒有跟隨逗號(hào)的單個(gè)模式 (例如 (3 | 4)) 是一個(gè) 分組模式。 而用方括號(hào)括起來的單個(gè)模式 (例如 [3 | 4]) 則仍是一個(gè)序列模式。

一個(gè)序列模式中最多可以有一個(gè)星號(hào)子模式。星號(hào)子模式可以出現(xiàn)在任何位置。如果沒有星號(hào)子模式,該序列模式是固定長(zhǎng)度的序列模式;否則,其是一個(gè)可變長(zhǎng)度的序列模式。

下面是將一個(gè)序列模式與一個(gè)主詞值相匹配的邏輯流程:

  1. 如果主詞值不是一個(gè)序列 2 ,該序列模式匹配失敗。

  2. 如果主詞值是 str 、 bytesbytearray 的實(shí)例,則該序列模式匹配失敗。

  3. 隨后的步驟取決于序列模式是固定長(zhǎng)度還是可變長(zhǎng)度的。

    如果序列模式是固定長(zhǎng)度的:

    1. 如果主詞序列的長(zhǎng)度與子模式的數(shù)量不相等,則該序列模式匹配失敗

    2. 序列模式中的子模式與主詞序列中的相應(yīng)項(xiàng)目從左到右進(jìn)行匹配。 一旦一個(gè)子模式匹配失敗,就停止匹配。 如果所有的子模式都成功地與它們的對(duì)應(yīng)項(xiàng)相匹配,那么該序列模式就匹配成功了。

    否則,如果序列模式是變長(zhǎng)的:

    1. 如果主詞序列的長(zhǎng)度小于非星號(hào)子模式的數(shù)量,則該序列模式匹配失敗。

    2. 與固定長(zhǎng)度的序列一樣,靠前的非星形子模式與其相應(yīng)的項(xiàng)目進(jìn)行匹配。

    3. 如果上一步成功,星號(hào)子模式與剩余的主詞項(xiàng)形成的列表相匹配,不包括星號(hào)子模式之后的非星號(hào)子模式所對(duì)應(yīng)的剩余項(xiàng)。

    4. 剩余的非星號(hào)子模式將與相應(yīng)的主詞項(xiàng)匹配,就像固定長(zhǎng)度的序列一樣。

    備注

    主詞序列的長(zhǎng)度可通過 len() (即通過 __len__() 協(xié)議) 獲得。 解釋器可能會(huì)以類似于 值模式 的方式緩存這個(gè)長(zhǎng)度信息。

簡(jiǎn)而言之, [P1, P2, P3, ... , P<N>] 僅在滿足以下情況時(shí)匹配成功:

  • 檢查 <subject> 是一個(gè)序列

  • len(subject) == <N>

  • P1<subject>[0] 進(jìn)行匹配(請(qǐng)注意此匹配可以綁定名稱)

  • P2<subject>[1] 進(jìn)行匹配(請(qǐng)注意此匹配可以綁定名稱)

  • …… 剩余對(duì)應(yīng)的模式/元素也以此類推。

8.6.4.9. 映射模式?

映射模式包含一個(gè)或多個(gè)鍵值模式。其語法類似于字典的構(gòu)造。語法:

mapping_pattern     ::=  "{" [items_pattern] "}"
items_pattern       ::=  ",".key_value_pattern+ ","?
key_value_pattern   ::=  (literal_pattern | value_pattern) ":" pattern
                         | double_star_pattern
double_star_pattern ::=  "**" capture_pattern

一個(gè)映射模式中最多可以有一個(gè)雙星號(hào)模式。雙星號(hào)模式必須是映射模式中的最后一個(gè)子模式。

映射模式中不允許出現(xiàn)重復(fù)的鍵。重復(fù)的字面值鍵會(huì)引發(fā) SyntaxError 。若是兩個(gè)鍵有相同的值將會(huì)在運(yùn)行時(shí)引發(fā) ValueError

以下是映射模式與主詞值匹配的邏輯流程:

  1. 如果主詞值不是一個(gè)映射 3,則映射模式匹配失敗。

  2. 若映射模式中給出的每個(gè)鍵都存在于主詞映射中,且每個(gè)鍵的模式都與主詞映射的相應(yīng)項(xiàng)匹配成功,則該映射模式匹配成功。

  3. 如果在映射模式中檢測(cè)到重復(fù)的鍵,該模式將被視作無效。對(duì)于重復(fù)的字面值,會(huì)引發(fā) SyntaxError ;對(duì)于相同值的命名鍵,會(huì)引發(fā) ValueError 。

備注

鍵值對(duì)使用映射主詞的 get() 方法的雙參數(shù)形式進(jìn)行匹配。匹配的鍵值對(duì)必須已經(jīng)存在于映射中,而不是通過 __missing__()__getitem__() 即時(shí)創(chuàng)建。

簡(jiǎn)而言之, {KEY1: P1, KEY2: P2, ... } 僅在滿足以下情況時(shí)匹配成功:

  • 檢查 <subject> 是映射

  • KEY1 in <subject>

  • P1<subject>[KEY1] 相匹配

  • …… 剩余對(duì)應(yīng)的鍵/模式對(duì)也以此類推。

8.6.4.10. 類模式?

類模式表示一個(gè)類以及它的位置參數(shù)和關(guān)鍵字參數(shù)(如果有的話)。語法:

class_pattern       ::=  name_or_attr "(" [pattern_arguments ","?] ")"
pattern_arguments   ::=  positional_patterns ["," keyword_patterns]
                         | keyword_patterns
positional_patterns ::=  ",".pattern+
keyword_patterns    ::=  ",".keyword_pattern+
keyword_pattern     ::=  NAME "=" pattern

同一個(gè)關(guān)鍵詞不應(yīng)該在類模式中重復(fù)出現(xiàn)。

The following is the logical flow for matching a class pattern against a subject value:

  1. 如果 name_or_attr 不是內(nèi)置 type 的實(shí)例,引發(fā) TypeError 。

  2. 如果主詞值不是 name_or_attr 的實(shí)例(通過 isinstance() 測(cè)試),該類模式匹配失敗。

  3. 如果沒有模式參數(shù)存在,則該模式匹配成功。 否則,后面的步驟取決于是否有關(guān)鍵字或位置參數(shù)模式存在。

    對(duì)于一些內(nèi)置的類型(將在后文詳述),接受一個(gè)位置子模式,它將與整個(gè)主詞值相匹配;對(duì)于這些類型,關(guān)鍵字模式也像其他類型一樣工作。

    如果只存在關(guān)鍵詞模式,它們將被逐一處理,如下所示:

    一. 該關(guān)鍵詞被視作主體的一個(gè)屬性進(jìn)行查找。

    • 如果這引發(fā)了除 AttributeError 以外的異常,該異常會(huì)被拋出。

    • 如果這引發(fā)了 AttributeError ,該類模式匹配失敗。

    • 否則,與關(guān)鍵詞模式相關(guān)的子模式將與主詞的屬性值進(jìn)行匹配。 如果失敗,則類模式匹配失?。蝗绻晒?,則繼續(xù)對(duì)下一個(gè)關(guān)鍵詞進(jìn)行匹配。

    二. 如果所有的關(guān)鍵詞模式匹配成功,該類模式匹配成功。

    如果存在位置模式,在匹配前會(huì)用類 name_or_attr__match_args__ 屬性將其轉(zhuǎn)換為關(guān)鍵詞模式。

    I. The equivalent of getattr(cls, "__match_args__", ()) is called.

    • 如果這引發(fā)一個(gè)異常,該異常將被拋出。

    • 如果返回值不是一個(gè)元組,則轉(zhuǎn)換失敗且引發(fā) TypeError

    • 若位置模式的數(shù)量超出 len(cls.__match_args__) ,將引發(fā) TypeError 。

    • 否則,位置模式 i 會(huì)使用 __match_args__[i] 轉(zhuǎn)換為關(guān)鍵詞。 __match_args__[i] 必須是一個(gè)字符串;如果不是則引發(fā) TypeError

    • 如果有重復(fù)的關(guān)鍵詞,引發(fā) TypeError

    二. 若所有的位置模式都被轉(zhuǎn)換為關(guān)鍵詞模式,

    匹配的過程就像只有關(guān)鍵詞模式一樣。

    對(duì)于以下內(nèi)置類型,位置子模式的處理是不同的:

    這些類接受一個(gè)位置參數(shù),其模式針對(duì)整個(gè)對(duì)象而不是某個(gè)屬性進(jìn)行匹配。例如, int(0|1) 匹配值 0 ,但不匹配值 0.0False 。

簡(jiǎn)而言之, CLS(P1, attr=P2) 僅在滿足以下情況時(shí)匹配成功:

  • isinstance(<subject>, CLS)

  • CLS.__match_args__P1 轉(zhuǎn)換為關(guān)鍵詞模式

  • 對(duì)于每個(gè)關(guān)鍵詞參數(shù) attr=P2
    • hasattr(<subject>, "attr")

    • P2<subject>.attr 進(jìn)行匹配

  • …… 剩余對(duì)應(yīng)的關(guān)鍵字參數(shù)/模式對(duì)也以此類推。

參見

  • PEP 634 —— 結(jié)構(gòu)化模式匹配:規(guī)范

  • PEP 636 —— 結(jié)構(gòu)化模式匹配:教程

8.7. 函數(shù)定義?

函數(shù)定義就是對(duì)用戶自定義函數(shù)的定義(參見 標(biāo)準(zhǔn)類型層級(jí)結(jié)構(gòu) 一節(jié)):

funcdef                   ::=  [decorators] "def" funcname "(" [parameter_list] ")"
                               ["->" expression] ":" suite
decorators                ::=  decorator+
decorator                 ::=  "@" assignment_expression NEWLINE
parameter_list            ::=  defparameter ("," defparameter)* "," "/" ["," [parameter_list_no_posonly]]
                                 | parameter_list_no_posonly
parameter_list_no_posonly ::=  defparameter ("," defparameter)* ["," [parameter_list_starargs]]
                               | parameter_list_starargs
parameter_list_starargs   ::=  "*" [parameter] ("," defparameter)* ["," ["**" parameter [","]]]
                               | "**" parameter [","]
parameter                 ::=  identifier [":" expression]
defparameter              ::=  parameter ["=" expression]
funcname                  ::=  identifier

函數(shù)定義是一條可執(zhí)行語句。 它執(zhí)行時(shí)會(huì)在當(dāng)前局部命名空間中將函數(shù)名稱綁定到一個(gè)函數(shù)對(duì)象(函數(shù)可執(zhí)行代碼的包裝器)。 這個(gè)函數(shù)對(duì)象包含對(duì)當(dāng)前全局命名空間的引用,作為函數(shù)被調(diào)用時(shí)所使用的全局命名空間。

函數(shù)定義并不會(huì)執(zhí)行函數(shù)體;只有當(dāng)函數(shù)被調(diào)用時(shí)才會(huì)執(zhí)行此操作。 4

一個(gè)函數(shù)定義可以被一個(gè)或多個(gè) decorator 表達(dá)式所包裝。 當(dāng)函數(shù)被定義時(shí)將在包含該函數(shù)定義的作用域中對(duì)裝飾器表達(dá)式求值。 求值結(jié)果必須是一個(gè)可調(diào)用對(duì)象,它會(huì)以該函數(shù)對(duì)象作為唯一參數(shù)被發(fā)起調(diào)用。 其返回值將被綁定到函數(shù)名稱而非函數(shù)對(duì)象。 多個(gè)裝飾器會(huì)以嵌套方式被應(yīng)用。 例如以下代碼

@f1(arg)
@f2
def func(): pass

大致等價(jià)于

def func(): pass
func = f1(arg)(f2(func))

不同之處在于原始函數(shù)并不會(huì)被臨時(shí)綁定到名稱 func。

在 3.9 版更改: Functions may be decorated with any valid assignment_expression. Previously, the grammar was much more restrictive; see PEP 614 for details.

當(dāng)一個(gè)或多個(gè) 形參 具有 形參 = 表達(dá)式 這樣的形式時(shí),該函數(shù)就被稱為具有“默認(rèn)形參值”。 對(duì)于一個(gè)具有默認(rèn)值的形參,其對(duì)應(yīng)的 argument 可以在調(diào)用中被省略,在此情況下會(huì)用形參的默認(rèn)值來替代。 如果一個(gè)形參具有默認(rèn)值,后續(xù)所有在 "*" 之前的形參也必須具有默認(rèn)值 --- 這個(gè)句法限制并未在語法中明確表達(dá)。

默認(rèn)形參值會(huì)在執(zhí)行函數(shù)定義時(shí)按從左至右的順序被求值。 這意味著當(dāng)函數(shù)被定義時(shí)將對(duì)表達(dá)式求值一次,相同的“預(yù)計(jì)算”值將在每次調(diào)用時(shí)被使用。 這一點(diǎn)在默認(rèn)形參為可變對(duì)象,例如列表或字典的時(shí)候尤其需要重點(diǎn)理解:如果函數(shù)修改了該對(duì)象(例如向列表添加了一項(xiàng)),則實(shí)際上默認(rèn)值也會(huì)被修改。 這通常不是人們所想要的。 繞過此問題的一個(gè)方法是使用 None 作為默認(rèn)值,并在函數(shù)體中顯式地對(duì)其進(jìn)測(cè)試,例如:

def whats_on_the_telly(penguin=None):
    if penguin is None:
        penguin = []
    penguin.append("property of the zoo")
    return penguin

函數(shù)調(diào)用的語義在 調(diào)用 一節(jié)中有更詳細(xì)的描述。 函數(shù)調(diào)用總是會(huì)給形參列表中列出的所有形參賦值,或是用位置參數(shù),或是用關(guān)鍵字參數(shù),或是用默認(rèn)值。 如果存在 "*identifier" 這樣的形式,它會(huì)被初始化為一個(gè)元組來接收任何額外的位置參數(shù),默認(rèn)為一個(gè)空元組。 如果存在 "**identifier" 這樣的形式,它會(huì)被初始化為一個(gè)新的有序映射來接收任何額外的關(guān)鍵字參數(shù),默認(rèn)為一個(gè)相同類型的空映射。 在 "*" 或 "*identifier" 之后的形參都是僅限關(guān)鍵字形參因而只能通過關(guān)鍵字參數(shù)傳入。 在 "/" 之前的形參都是僅限位置形參因而只能通過位置參數(shù)傳入。

在 3.8 版更改: 可以使用 / 函數(shù)形參語法來標(biāo)示僅限位置形參。 請(qǐng)參閱 PEP 570 了解詳情。

形參可以帶有 標(biāo)注,其形式為在形參名稱后加上 ": expression"。 任何形參都可以帶有標(biāo)注,甚至 *identifier**identifier 這樣的形參也可以。 函數(shù)可以帶有“返回”標(biāo)注,其形式為在形參列表后加上 "-> expression"。 這些標(biāo)注可以是任何有效的 Python 表達(dá)式。 標(biāo)注的存在不會(huì)改變函數(shù)的語義。 標(biāo)注值可以作為函數(shù)對(duì)象的 __annotations__ 屬性中以對(duì)應(yīng)形參名稱為鍵的字典值被訪問。 如果使用了 annotations import from __future__ 的方式,則標(biāo)注會(huì)在運(yùn)行時(shí)保存為字符串以啟用延遲求值特性。 否則,它們會(huì)在執(zhí)行函數(shù)定義時(shí)被求值。 在這種情況下,標(biāo)注的求值順序可能與它們?cè)谠创a中出現(xiàn)的順序不同。

創(chuàng)建匿名函數(shù)(未綁定到一個(gè)名稱的函數(shù))以便立即在表達(dá)式中使用也是可能的。 這需要使用 lambda 表達(dá)式,具體描述見 lambda 表達(dá)式 一節(jié)。 請(qǐng)注意 lambda 只是簡(jiǎn)單函數(shù)定義的一種簡(jiǎn)化寫法;在 "def" 語句中定義的函數(shù)也可以像用 lambda 表達(dá)式定義的函數(shù)一樣被傳遞或賦值給其他名稱。 "def" 形式實(shí)際上更為強(qiáng)大,因?yàn)樗试S執(zhí)行多條語句和使用標(biāo)注。

程序員注意事項(xiàng): 函數(shù)屬于一類對(duì)象。 在一個(gè)函數(shù)內(nèi)部執(zhí)行的 "def" 語句會(huì)定義一個(gè)局部函數(shù)并可被返回或傳遞。 在嵌套函數(shù)中使用的自由變量可以訪問包含該 def 語句的函數(shù)的局部變量。 詳情參見 命名與綁定 一節(jié)。

參見

PEP 3107 - 函數(shù)標(biāo)注

最初的函數(shù)標(biāo)注規(guī)范說明。

PEP 484 - 類型提示

標(biāo)注的標(biāo)準(zhǔn)含意定義:類型提示。

PEP 526 - 變量標(biāo)注的語法

變量聲明的類型提示功能,包括類變量和實(shí)例變量

PEP 563 - 延遲的標(biāo)注求值

支持在運(yùn)行時(shí)通過以字符串形式保存標(biāo)注而非不是即求值來實(shí)現(xiàn)標(biāo)注內(nèi)部的向前引用。

8.8. 類定義?

類定義就是對(duì)類對(duì)象的定義 (參見 標(biāo)準(zhǔn)類型層級(jí)結(jié)構(gòu) 一節(jié)):

classdef    ::=  [decorators] "class" classname [inheritance] ":" suite
inheritance ::=  "(" [argument_list] ")"
classname   ::=  identifier

類定義是一條可執(zhí)行語句。 其中繼承列表通常給出基類的列表 (進(jìn)階用法請(qǐng)參見 元類),列表中的每一項(xiàng)都應(yīng)當(dāng)被求值為一個(gè)允許子類的類對(duì)象。 沒有繼承列表的類默認(rèn)繼承自基類 object;因此,:

class Foo:
    pass

等價(jià)于

class Foo(object):
    pass

隨后類體將在一個(gè)新的執(zhí)行幀 (參見 命名與綁定) 中被執(zhí)行,使用新創(chuàng)建的局部命名空間和原有的全局命名空間。 (通常,類體主要包含函數(shù)定義。) 當(dāng)類體結(jié)束執(zhí)行時(shí),其執(zhí)行幀將被丟棄而其局部命名空間會(huì)被保存。 5 一個(gè)類對(duì)象隨后會(huì)被創(chuàng)建,其基類使用給定的繼承列表,屬性字典使用保存的局部命名空間。 類名稱將在原有的全局命名空間中綁定到該類對(duì)象。

在類體內(nèi)定義的屬性的順序保存在新類的 __dict__ 中。 請(qǐng)注意此順序的可靠性只限于類剛被創(chuàng)建時(shí),并且只適用于使用定義語法所定義的類。

類的創(chuàng)建可使用 元類 進(jìn)行重度定制。

類也可以被裝飾:就像裝飾函數(shù)一樣,:

@f1(arg)
@f2
class Foo: pass

大致等價(jià)于

class Foo: pass
Foo = f1(arg)(f2(Foo))

裝飾器表達(dá)式的求值規(guī)則與函數(shù)裝飾器相同。 結(jié)果隨后會(huì)被綁定到類名稱。

在 3.9 版更改: Classes may be decorated with any valid assignment_expression. Previously, the grammar was much more restrictive; see PEP 614 for details.

程序員注意事項(xiàng): 在類定義內(nèi)定義的變量是類屬性;它們將被類實(shí)例所共享。 實(shí)例屬性可通過 self.name = value 在方法中設(shè)定。 類和實(shí)例屬性均可通過 "self.name" 表示法來訪問,當(dāng)通過此方式訪問時(shí)實(shí)例屬性會(huì)隱藏同名的類屬性。 類屬性可被用作實(shí)例屬性的默認(rèn)值,但在此場(chǎng)景下使用可變值可能導(dǎo)致未預(yù)期的結(jié)果。 可以使用 描述器 來創(chuàng)建具有不同實(shí)現(xiàn)細(xì)節(jié)的實(shí)例變量。

參見

PEP 3115 - Python 3000 中的元類

將元類聲明修改為當(dāng)前語法的提議,以及關(guān)于如何構(gòu)建帶有元類的類的語義描述。

PEP 3129 - 類裝飾器

增加類裝飾器的提議。 函數(shù)和方法裝飾器是在 PEP 318 中被引入的。

8.9. 協(xié)程?

3.5 新版功能.

8.9.1. 協(xié)程函數(shù)定義?

async_funcdef ::=  [decorators] "async" "def" funcname "(" [parameter_list] ")"
                   ["->" expression] ":" suite

Python 協(xié)程的執(zhí)行可以在多個(gè)位置上被掛起和恢復(fù) (參見 coroutine)。 await 表達(dá)式,async for 以及 async with 只能在協(xié)程函數(shù)體中使用。

使用 async def 語法定義的函數(shù)總是為協(xié)程函數(shù),即使它們不包含 awaitasync 關(guān)鍵字。

在協(xié)程函數(shù)體中使用 yield from 表達(dá)式將引發(fā) SyntaxError

協(xié)程函數(shù)的例子:

async def func(param1, param2):
    do_stuff()
    await some_coroutine()

在 3.7 版更改: awaitasync 現(xiàn)在是保留關(guān)鍵字;在之前版本中它們僅在協(xié)程函數(shù)內(nèi)被當(dāng)作保留關(guān)鍵字。

8.9.2. async for 語句?

async_for_stmt ::=  "async" for_stmt

asynchronous iterable 提供了 __aiter__ 方法,該方法會(huì)直接返回 asynchronous iterator,它可以在其 __anext__ 方法中調(diào)用異步代碼。

async for 語句允許方便地對(duì)異步可迭代對(duì)象進(jìn)行迭代。

以下代碼:

async for TARGET in ITER:
    SUITE
else:
    SUITE2

在語義上等價(jià)于:

iter = (ITER)
iter = type(iter).__aiter__(iter)
running = True

while running:
    try:
        TARGET = await type(iter).__anext__(iter)
    except StopAsyncIteration:
        running = False
    else:
        SUITE
else:
    SUITE2

另請(qǐng)參閱 __aiter__()__anext__() 了解詳情。

在協(xié)程函數(shù)體之外使用 async for 語句將引發(fā) SyntaxError

8.9.3. async with 語句?

async_with_stmt ::=  "async" with_stmt

asynchronous context manager 是一種 context manager,能夠在其 enterexit 方法中暫停執(zhí)行。

以下代碼:

async with EXPRESSION as TARGET:
    SUITE

在語義上等價(jià)于:

manager = (EXPRESSION)
aenter = type(manager).__aenter__
aexit = type(manager).__aexit__
value = await aenter(manager)
hit_except = False

try:
    TARGET = value
    SUITE
except:
    hit_except = True
    if not await aexit(manager, *sys.exc_info()):
        raise
finally:
    if not hit_except:
        await aexit(manager, None, None, None)

另請(qǐng)參閱 __aenter__()__aexit__() 了解詳情。

在協(xié)程函數(shù)體之外使用 async with 語句將引發(fā) SyntaxError。

參見

PEP 492 - 使用 async 和 await 語法實(shí)現(xiàn)協(xié)程

將協(xié)程作為 Python 中的一個(gè)正式的單獨(dú)概念,并增加相應(yīng)的支持語法。

備注

1

異常會(huì)被傳播給發(fā)起調(diào)用棧,除非存在一個(gè) finally 子句正好引發(fā)了另一個(gè)異常。 新引發(fā)的異常將導(dǎo)致舊異常的丟失。

2

在模式匹配中,序列被定義為以下幾種之一:

下列標(biāo)準(zhǔn)庫(kù)中的類都是序列:

備注

類型為 str, bytesbytearray 的目標(biāo)值不能匹配序列模式。

3

在模式匹配中,映射被定義為以下幾種之一:

標(biāo)準(zhǔn)庫(kù)中的 dicttypes.MappingProxyType 類都屬于映射。

4

作為函數(shù)體的第一條語句出現(xiàn)的字符串字面值會(huì)被轉(zhuǎn)換為函數(shù)的 __doc__ 屬性,也就是該函數(shù)的 docstring。

5

作為類體的第一條語句出現(xiàn)的字符串字面值會(huì)被轉(zhuǎn)換為命名空間的 __doc__ 條目,也就是該類的 docstring。