8. 復(fù)合語句?

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

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

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

if test1: if test2: print(x)

還要注意的是在這種情形下分號的綁定比冒號更緊密,因此在以下示例中,所有 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)* [";"]

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

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

8.1. if 語句?

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

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

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

8.2. while 語句?

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

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

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

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

8.3. for 語句?

for 語句用于對序列(例如字符串、元組或列表)或其他可迭代對象中的元素進(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.

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

for 循環(huán)會對目標(biāo)列表中的變量進(jìn)行賦值。 這將覆蓋之前對這些變量的所有賦值,包括在 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é)束時不會被刪除,但如果序列為空,則它們根本不會被循環(huán)所賦值。 提示:內(nèi)置函數(shù) range() 會返回一個可迭代的整數(shù)序列,適用于模擬 Pascal 中的 for i := a to b do 這種效果;例如 list(range(3)) 會返回列表 [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 子句與異常相匹配,則會在周邊代碼和發(fā)起調(diào)用棧上繼續(xù)搜索異常處理器。 1

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

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

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

except E as N:
    foo

被轉(zhuǎn)寫為

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

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

在一個 except 子句體被執(zhí)行之前,有關(guān)異常的詳細(xì)信息存放在 sys 模塊中,可通過 sys.exc_info() 來訪問。 sys.exc_info() 返回一個 3 元組,由異常類、異常實(shí)例和回溯對象組成(參見 標(biāo)準(zhǔn)類型層級結(jié)構(gòu) 一節(jié)),用于在程序中標(biāo)識異常發(fā)生點(diǎn)。 當(dāng)從處理異常的代碼返回時,通過 sys.exc_info() 訪問的異常詳細(xì)信息會恢復(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 子句體時沒有引發(fā)異常,并且沒有執(zhí)行 return, continuebreak 語句,可選的 else 子句將被執(zhí)行。 else 語句中的異常不會由之前的 except 子句處理。

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

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

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

當(dāng) return, breakcontinue 語句在一個 try...finally 語句的 try 子語句體中被執(zhí)行時,finally 子語句也會‘在離開時’被執(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)存在一個問題。

8.5. with 語句?

with 語句用于包裝帶有使用上下文管理器 (參見 with 語句上下文管理器 一節(jié)) 定義的方法的代碼塊的執(zhí)行。 這允許對普通的 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]

帶有一個“項(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 語句中包含一個目標(biāo),來自 __enter__() 的返回值將被賦值給它。

    備注

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

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

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

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

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

以下代碼:

with EXPRESSION as TARGET:
    SUITE

在語義上等價于:

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)

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

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

在語義上等價于:

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

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

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

在 3.1 版更改: 支持多個上下文表達(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é)使用單引號來表示 軟關(guān)鍵字。

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

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

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

關(guān)鍵字 matchcasesoft keywords 。

參見

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

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

8.6.1. 概述?

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

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

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

    備注

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

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

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

    • 否則,將按照上述方法嘗試下一個 case_block 。

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

備注

用戶一般不應(yīng)依賴正在求值的模式。 根據(jù)不同的實(shí)現(xiàn)方式,解釋器可能會緩存數(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

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

8.6.2. 約束項(xiàng)?

guard ::=  "if" named_expression

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

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

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

  2. 如果該模式匹配成功,對 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.

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

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

8.6.3. 必定匹配的 case 塊?

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

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

8.6.4. 模式?

備注

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

  • 符號 SEP.RULE+RULE (SEP RULE)* 的簡寫

  • 符號 !RULE 是前向否定斷言的簡寫

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

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

8.6.4.1. 或模式?

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

or_pattern ::=  "|".closed_pattern+

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

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

簡而言之,P1 | P2 | ... 會首先嘗試匹配 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 不能是 _ 。

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

8.6.4.3. 字面值模式?

字面值模式對應(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 中定義的。支持三引號的字符串。不支持原始字符串和字節(jié)字符串。也不支持 格式字符串字面值 。

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

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

8.6.4.4. 捕獲模式?

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

capture_pattern ::=  !'_' NAME

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

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

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

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

8.6.4.5. 通配符模式?

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

wildcard_pattern ::=  '_'

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

簡而言之,_ 總是會匹配成功。

8.6.4.6. 值模式?

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

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

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

簡而言之, NAME1.NAME2 僅在 <subject> == NAME1.NAME2 時匹配成功。

備注

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

8.6.4.7. 組模式?

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

group_pattern ::=  "(" pattern ")"

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

8.6.4.8. 序列模式?

一個序列模式包含數(shù)個將與序列元素進(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)

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

備注

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

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

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

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

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

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

    如果序列模式是固定長度的:

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

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

    否則,如果序列模式是變長的:

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

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

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

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

    備注

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

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

  • 檢查 <subject> 是一個序列

  • len(subject) == <N>

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

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

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

8.6.4.9. 映射模式?

映射模式包含一個或多個鍵值模式。其語法類似于字典的構(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

一個映射模式中最多可以有一個雙星號模式。雙星號模式必須是映射模式中的最后一個子模式。

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

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

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

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

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

備注

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

簡而言之, {KEY1: P1, KEY2: P2, ... } 僅在滿足以下情況時匹配成功:

  • 檢查 <subject> 是映射

  • KEY1 in <subject>

  • P1<subject>[KEY1] 相匹配

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

8.6.4.10. 類模式?

類模式表示一個類以及它的位置參數(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

同一個關(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() 測試),該類模式匹配失敗。

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

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

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

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

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

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

    • 否則,與關(guān)鍵詞模式相關(guān)的子模式將與主詞的屬性值進(jìn)行匹配。 如果失敗,則類模式匹配失敗;如果成功,則繼續(xù)對下一個關(guān)鍵詞進(jìn)行匹配。

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

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

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

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

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

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

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

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

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

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

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

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

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

  • isinstance(<subject>, CLS)

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

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

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

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

參見

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

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

8.7. 函數(shù)定義?

函數(shù)定義就是對用戶自定義函數(shù)的定義(參見 標(biāo)準(zhǔn)類型層級結(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í)行時會在當(dāng)前局部命名空間中將函數(shù)名稱綁定到一個函數(shù)對象(函數(shù)可執(zhí)行代碼的包裝器)。 這個函數(shù)對象包含對當(dāng)前全局命名空間的引用,作為函數(shù)被調(diào)用時所使用的全局命名空間。

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

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

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

大致等價于

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

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

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

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)用總是會給形參列表中列出的所有形參賦值,或是用位置參數(shù),或是用關(guān)鍵字參數(shù),或是用默認(rèn)值。 如果存在 "*identifier" 這樣的形式,它會被初始化為一個元組來接收任何額外的位置參數(shù),默認(rèn)為一個空元組。 如果存在 "**identifier" 這樣的形式,它會被初始化為一個新的有序映射來接收任何額外的關(guān)鍵字參數(shù),默認(rèn)為一個相同類型的空映射。 在 "*" 或 "*identifier" 之后的形參都是僅限關(guān)鍵字形參因而只能通過關(guān)鍵字參數(shù)傳入。 在 "/" 之前的形參都是僅限位置形參因而只能通過位置參數(shù)傳入。

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

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

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

程序員注意事項(xiàng): 函數(shù)屬于一類對象。 在一個函數(shù)內(nèi)部執(zhí)行的 "def" 語句會定義一個局部函數(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)行時通過以字符串形式保存標(biāo)注而非不是即求值來實(shí)現(xiàn)標(biāo)注內(nèi)部的向前引用。

8.8. 類定義?

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

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

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

class Foo:
    pass

等價于

class Foo(object):
    pass

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

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

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

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

@f1(arg)
@f2
class Foo: pass

大致等價于

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

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

在 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í)例屬性的默認(rèn)值,但在此場景下使用可變值可能導(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í)行可以在多個位置上被掛起和恢復(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__ 方法,該方法會直接返回 asynchronous iterator,它可以在其 __anext__ 方法中調(diào)用異步代碼。

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

以下代碼:

async for TARGET in ITER:
    SUITE
else:
    SUITE2

在語義上等價于:

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

另請參閱 __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

在語義上等價于:

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)

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

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

參見

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

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

備注

1

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

2

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

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

備注

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

3

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

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

4

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

5

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