8. 復合語(yǔ)句?
復合語(yǔ)句是包含其它語(yǔ)句(語(yǔ)句組)的語(yǔ)句;它們會(huì )以某種方式影響或控制所包含其它語(yǔ)句的執行。 通常,復合語(yǔ)句會(huì )跨越多行,雖然在某些簡(jiǎn)單形式下整個(gè)復合語(yǔ)句也可能包含于一行之內。
if
, while
和 for
語(yǔ)句用來(lái)實(shí)現傳統的控制流程構造。 try
語(yǔ)句為一組語(yǔ)句指定異常處理和/和清理代碼,而 with
語(yǔ)句允許在一個(gè)代碼塊周?chē)鷪绦谐跏蓟徒K結化代碼。 函數和類(lèi)定義在語(yǔ)法上也屬于復合語(yǔ)句。
一條復合語(yǔ)句由一個(gè)或多個(gè)‘子句’組成。 一個(gè)子句則包含一個(gè)句頭和一個(gè)‘句體’。 特定復合語(yǔ)句的子句頭都處于相同的縮進(jìn)層級。 每個(gè)子句頭以一個(gè)作為唯一標識的關(guān)鍵字開(kāi)始并以一個(gè)冒號結束。 子句體是由一個(gè)子句控制的一組語(yǔ)句。 子句體可以是在子句頭的冒號之后與其同處一行的一條或由分號分隔的多條簡(jiǎn)單語(yǔ)句,或者也可以是在其之后縮進(jìn)的一行或多行語(yǔ)句。 只有后一種形式的子句體才能包含嵌套的復合語(yǔ)句;以下形式是不合法的,這主要是因為無(wú)法分清某個(gè)后續的 else
子句應該屬于哪個(gè) if
子句:
if test1: if test2: print(x)
還要注意的是在這種情形下分號的綁定比冒號更緊密,因此在以下示例中,所有 print()
調用或者都不執行,或者都執行:
if x < y < z: print(x); print(y); print(z)
總結:
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 INDENTstatement
+ DEDENT statement ::=stmt_list
NEWLINE |compound_stmt
stmt_list ::=simple_stmt
(";"simple_stmt
)* [";"]
請注意語(yǔ)句總是以 NEWLINE
結束,之后可能跟隨一個(gè) DEDENT
。 還要注意可選的后續子句總是以一個(gè)不能作為語(yǔ)句開(kāi)頭的關(guān)鍵字作為開(kāi)頭,因此不會(huì )產(chǎn)生歧義(‘懸空的 else
’問(wèn)題在 Python 中是通過(guò)要求嵌套的 if
語(yǔ)句必須縮進(jìn)來(lái)解決的)。
為了保證清晰,以下各節中語(yǔ)法規則采用將每個(gè)子句都放在單獨行中的格式。
8.1. if
語(yǔ)句?
if
語(yǔ)句用于有條件的執行:
if_stmt ::= "if"assignment_expression
":"suite
("elif"assignment_expression
":"suite
)* ["else" ":"suite
]
它通過(guò)對表達式逐個(gè)求值直至找到一個(gè)真值(請參閱 布爾運算 了解真值與假值的定義)在子句體中選擇唯一匹配的一個(gè);然后執行該子句體(而且 if
語(yǔ)句的其他部分不會(huì )被執行或求值)。 如果所有表達式均為假值,則如果 else
子句體如果存在就會(huì )被執行。
8.2. while
語(yǔ)句?
while
語(yǔ)句用于在表達式保持為真的情況下重復地執行:
while_stmt ::= "while"assignment_expression
":"suite
["else" ":"suite
]
這將重復地檢驗表達式,并且如果其值為真就執行第一個(gè)子句體;如果表達式值為假(這可能在第一次檢驗時(shí)就發(fā)生)則如果 else
子句體存在就會(huì )被執行并終止循環(huán)。
第一個(gè)子句體中的 break
語(yǔ)句在執行時(shí)將終止循環(huán)且不執行 else
子句體。 第一個(gè)子句體中的 continue
語(yǔ)句在執行時(shí)將跳過(guò)子句體中的剩余部分并返回檢驗表達式。
8.3. for
語(yǔ)句?
for
語(yǔ)句用于對序列(例如字符串、元組或列表)或其他可迭代對象中的元素進(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 賦值語(yǔ)句), 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
語(yǔ)句在執行時(shí)將終止循環(huán)且不執行 else
子句體。 第一個(gè)子句體中的 continue
語(yǔ)句在執行時(shí)將跳過(guò)子句體中的剩余部分并轉往下一項繼續執行,或者在沒(méi)有下一項時(shí)轉往 else
子句執行。
for 循環(huán)會(huì )對目標列表中的變量進(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
目標列表中的名稱(chēng)在循環(huán)結束時(shí)不會(huì )被刪除,但如果序列為空,則它們根本不會(huì )被循環(huán)所賦值。 提示:內置函數 range()
會(huì )返回一個(gè)可迭代的整數序列,適用于模擬 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
語(yǔ)句?
try
語(yǔ)句可為一組語(yǔ)句指定異常處理器和/或清理代碼:
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.
如果沒(méi)有 except 子句與異常相匹配,則會(huì )在周邊代碼和發(fā)起調用棧上繼續搜索異常處理器。 1
如果在對 except 子句頭中的表達式求值時(shí)引發(fā)了異常,則原來(lái)對處理器的搜索會(huì )被取消,并在周邊代碼和調用棧上啟動(dòng)對新異常的搜索(它會(huì )被視作是整個(gè) try
語(yǔ)句所引發(fā)的異常)。
當找到一個(gè)匹配的 except 子句時(shí),該異常將被賦值給該 except 子句在 as
關(guān)鍵字之后指定的目標,如果存在此關(guān)鍵字的話(huà),并且該 except 子句體將被執行。 所有 except 子句都必須有可執行的子句體。 當到達子句體的末尾時(shí),通常會(huì )轉向整個(gè) try 語(yǔ)句之后繼續執行。 (這意味著(zhù)如果對于同一異常存在有嵌套的兩個(gè)處理器,而異常發(fā)生于內層處理器的 try 子句中,則外層處理器將不會(huì )處理該異常。)
當使用 as
將目標賦值為一個(gè)異常時(shí),它將在 except 子句結束時(shí)被清除。 這就相當于
except E as N:
foo
被轉寫(xiě)為
except E as N:
try:
foo
finally:
del N
這意味著(zhù)異常必須賦值給一個(gè)不同的名稱(chēng)才能在 except 子句之后引用它。 異常會(huì )被清除是因為在附加了回溯信息的情況下,它們會(huì )形成堆棧幀的循環(huán)引用,使得所有局部變量保持存活直到發(fā)生下一次垃圾回收。
在一個(gè) except 子句體被執行之前,有關(guān)異常的詳細信息存放在 sys
模塊中,可通過(guò) sys.exc_info()
來(lái)訪(fǎng)問(wèn)。 sys.exc_info()
返回一個(gè) 3 元組,由異常類(lèi)、異常實(shí)例和回溯對象組成(參見(jiàn) 標準類(lèi)型層級結構 一節),用于在程序中標識異常發(fā)生點(diǎn)。 當從處理異常的代碼返回時(shí),通過(guò) sys.exc_info()
訪(fǎng)問(wèn)的異常詳細信息會(huì )恢復到之前的值:
>>> 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
ExceptionGroup
s. 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.
如果控制流離開(kāi) try
子句體時(shí)沒(méi)有引發(fā)異常,并且沒(méi)有執行 return
, continue
或 break
語(yǔ)句,可選的 else
子句將被執行。 else
語(yǔ)句中的異常不會(huì )由之前的 except
子句處理。
如果存在 finally
,它將指定一個(gè)‘清理’處理程序。 try
子句會(huì )被執行,包括任何 except
和 else
子句。 如果在這些子句中發(fā)生任何未處理的異常,該異常會(huì )被臨時(shí)保存。 finally
子句將被執行。 如果存在被保存的異常,它會(huì )在 finally
子句的末尾被重新引發(fā)。 如果 finally
子句引發(fā)了另一個(gè)異常,被保存的異常會(huì )被設為新異常的上下文。 如果 finally
子句執行了 return
, break
或 continue
語(yǔ)句,則被保存的異常會(huì )被丟棄:
>>> def f():
... try:
... 1/0
... finally:
... return 42
...
>>> f()
42
在 finally
子句執行期間,程序不能獲取異常信息。
當 return
, break
或 continue
語(yǔ)句在一個(gè) try
...finally
語(yǔ)句的 try
子語(yǔ)句體中被執行時(shí),finally
子語(yǔ)句也會(huì )‘在離開(kāi)時(shí)’被執行。
函數的返回值是由最后被執行的 return
語(yǔ)句所決定的。 由于 finally
子句總是被執行,因此在 finally
子句中被執行的 return
語(yǔ)句總是最后被執行的:
>>> def foo():
... try:
... return 'try'
... finally:
... return 'finally'
...
>>> foo()
'finally'
有關(guān)異常的更多信息可以在 異常 一節找到,有關(guān)使用 raise
語(yǔ)句生成異常的信息可以在 raise 語(yǔ)句 一節找到。
8.5. with
語(yǔ)句?
with
語(yǔ)句用于包裝帶有使用上下文管理器 (參見(jiàn) with 語(yǔ)句上下文管理器 一節) 定義的方法的代碼塊的執行。 這允許對普通的 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è)“項目”的 with
語(yǔ)句的執行過(guò)程如下:
The context expression (the expression given in the
with_item
) is evaluated to obtain a context manager.載入上下文管理器的
__enter__()
以便后續使用。載入上下文管理器的
__exit__()
以便后續使用。發(fā)起調用上下文管理器的
__enter__()
方法。如果
with
語(yǔ)句中包含一個(gè)目標,來(lái)自__enter__()
的返回值將被賦值給它。備注
with
語(yǔ)句會(huì )保證如果__enter__()
方法返回時(shí)未發(fā)生錯誤,則__exit__()
將總是被調用。 因此,如果在對目標列表賦值期間發(fā)生錯誤,則會(huì )將其視為在語(yǔ)句體內部發(fā)生的錯誤。 參見(jiàn)下面的第 6 步。執行語(yǔ)句體。
發(fā)起調用上下文管理器的
__exit__()
方法。 如果語(yǔ)句體的退出是由異常導致的,則其類(lèi)型、值和回溯信息將被作為參數傳遞給__exit__()
。 否則的話(huà),將提供三個(gè)None
參數。如果語(yǔ)句體的退出是由異常導致的,并且來(lái)自
__exit__()
方法的返回值為假,則該異常會(huì )被重新引發(fā)。 如果返回值為真,則該異常會(huì )被抑制,并會(huì )繼續執行with
語(yǔ)句之后的語(yǔ)句。如果語(yǔ)句體由于異常以外的任何原因退出,則來(lái)自
__exit__()
的返回值會(huì )被忽略,并會(huì )在該類(lèi)退出正常的發(fā)生位置繼續執行。
以下代碼:
with EXPRESSION as TARGET:
SUITE
在語(yǔ)義上等價(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è)項目,則會(huì )視作存在多個(gè) with
語(yǔ)句嵌套來(lái)處理多個(gè)上下文管理器:
with A() as a, B() as b:
SUITE
在語(yǔ)義上等價(jià)于:
with A() as a:
with B() as b:
SUITE
也可以用圓括號包圍的多行形式的多項目上下文管理器。例如:
with (
A() as a,
B() as b,
):
SUITE
在 3.1 版更改: 支持多個(gè)上下文表達式。
在 3.10 版更改: Support for using grouping parentheses to break the statement in multiple lines.
8.6. match
語(yǔ)句?
3.10 新版功能.
匹配語(yǔ)句用于進(jìn)行模式匹配。語(yǔ)法如下:
match_stmt ::= 'match'subject_expr
":" NEWLINE INDENTcase_block
+ DEDENT subject_expr ::=star_named_expression
","star_named_expressions
? |named_expression
case_block ::= 'case'patterns
[guard
] ":"block
備注
本節使用單引號來(lái)表示 軟關(guān)鍵字。
模式匹配接受一個(gè)模式作為輸入(跟在 case
后),一個(gè)主詞值(跟在 match
后)。該模式(可能包含子模式)與主題值進(jìn)行匹配。輸出是:
匹配成功或失?。ㄒ脖环Q(chēng)為模式成功或失?。?。
可能將匹配的值綁定到一個(gè)名字上。 這方面的先決條件將在下面進(jìn)一步討論。
關(guān)鍵字 match
和 case
是 soft keywords 。
8.6.1. 概述?
匹配語(yǔ)句邏輯流程的概述如下:
對主詞表達式
subject_expr
求值后將結果作為匹配用的結果主詞值。 如果主詞表達式包含逗號,則使用 the standard rules 構建一個(gè)元組。主詞值將依次與
case_block
中的每個(gè)模式進(jìn)行匹配。匹配成功或失敗的具體規則在下面描述。匹配嘗試也可以與模式中的一些或所有的獨立名稱(chēng)綁定。準確的模式綁定規則因模式類(lèi)型而異,具體規定見(jiàn)下文。成功的模式匹配過(guò)程中產(chǎn)生的名稱(chēng)綁定將超越所執行的塊的范圍,可以在匹配語(yǔ)句之后使用。備注
在模式匹配失敗時(shí),一些子模式可能會(huì )成功。 不要依賴(lài)于失敗匹配進(jìn)行的綁定。 反過(guò)來(lái)說(shuō),不要認為變量在匹配失敗后保持不變。 確切的行為取決于實(shí)現,可能會(huì )有所不同。 這是一個(gè)有意的決定,允許不同的實(shí)現添加優(yōu)化。
如果該模式匹配成功,并且完成了對相應的約束項(如果存在)的求值。在這種情況下,保證完成所有的名稱(chēng)綁定。
If the guard evaluates as true or is missing, the
block
insidecase_block
is executed.否則,將按照上述方法嘗試下一個(gè)
case_block
。如果沒(méi)有進(jìn)一步的 case 塊,匹配語(yǔ)句終止。
備注
用戶(hù)一般不應依賴(lài)正在求值的模式。 根據不同的實(shí)現方式,解釋器可能會(huì )緩存數值或使用其他優(yōu)化方法來(lái)避免重復求值。
匹配語(yǔ)句示例:
>>> 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
是約束項。請閱讀下一節以了解更多相關(guān)內容。
8.6.2. 約束項?
guard ::= "if" named_expression
guard
(它是 case
的一部分) 必須成立才能讓 case
語(yǔ)句塊中的代碼被執行。 它所采用的形式為: if
之后跟一個(gè)表達式。
擁有 guard
的 case
塊的邏輯流程如下:
檢查
case
塊中的模式是否匹配成功。如果該模式匹配失敗,則不對guard
進(jìn)行求值,檢查下一個(gè)case
塊。如果該模式匹配成功,對
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
求值過(guò)程中引發(fā)了異常,則異常將被拋出。
允許約束項產(chǎn)生副作用,因為他們是表達式。約束項求值必須從第一個(gè) case 塊到最后一個(gè) case 塊依次逐個(gè)進(jìn)行,模式匹配失敗的 case 塊將被跳過(guò)。(也就是說(shuō),約束項求值必須按順序進(jìn)行。)一旦選用了一個(gè) case 塊,約束項求值必須由此終止。
8.6.3. 必定匹配的 case 塊?
必定匹配的 case 塊是能匹配所有情況的 case 塊。一個(gè)匹配語(yǔ)句最多可以有一個(gè)必定匹配的 case 塊,而且必須是最后一個(gè)。
如果一個(gè) case 塊沒(méi)有約束項,并且其模式是必定匹配的,那么它就被認為是必定匹配的。 如果我們可以?xún)H從語(yǔ)法上證明一個(gè)模式總是能匹配成功,那么這個(gè)模式就被認為是必定匹配的。 只有以下模式是必定匹配的:
8.6.4. 模式?
備注
本節使用了超出標準 EBNF 的語(yǔ)法符號。
符號
SEP.RULE+
是RULE (SEP RULE)*
的簡(jiǎn)寫(xiě)符號
!RULE
是前向否定斷言的簡(jiǎn)寫(xiě)
patterns
的頂層語(yǔ)法是:
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)而言之”以描述模式的作用,便于說(shuō)明問(wèn)題(感謝 Raymond Hettinger 提供的一份文件,大部分的描述受其啟發(fā))。請注意,這些描述純粹是為了說(shuō)明問(wèn)題,可能不 反映底層的實(shí)現。此外,它們并沒(méi)有涵蓋所有有效的形式。
8.6.4.1. 或模式?
或模式是由豎杠 |
分隔的兩個(gè)或更多的模式。語(yǔ)法:
or_pattern ::= "|".closed_pattern
+
只有最后的子模式可以是 必定匹配的,且每個(gè)子模式必須綁定相同的名字集以避免歧義。
或模式將主詞值依次與其每個(gè)子模式嘗試匹配,直到有一個(gè)匹配成功,然后該或模式被視作匹配成功。 否則,如果沒(méi)有任何子模式匹配成功,則或模式匹配失敗。
簡(jiǎn)而言之,P1 | P2 | ...
會(huì )首先嘗試匹配 P1
,如果失敗將接著(zhù)嘗試匹配 P2
,如果出現成功的匹配則立即結束且模式匹配成功,否則模式匹配失敗。
8.6.4.2. AS 模式?
AS 模式將關(guān)鍵字 as
左側的或模式與主詞值進(jìn)行匹配。語(yǔ)法:
as_pattern ::=or_pattern
"as"capture_pattern
如果或模式匹配失敗,AS 模式也匹配失敗。 否則,AS 模式將主詞與關(guān)鍵字 as 右邊的名字綁定且匹配陳成功。 capture_pattern
不能是 _
。
簡(jiǎn)而言之, P as NAME
將與 P
匹配,成功后將設置 NAME = <subject>
。
8.6.4.3. 字面值模式?
字面值模式對應 Python 中的大多數 字面值。 語(yǔ)法為:
literal_pattern ::=signed_number
|signed_number
"+" NUMBER |signed_number
"-" NUMBER |strings
| "None" | "True" | "False" |signed_number
: NUMBER | "-" NUMBER
規則 strings
和標記 NUMBER
是在 standard Python grammar 中定義的。支持三引號的字符串。不支持原始字符串和字節字符串。也不支持 格式字符串字面值 。
signed_number '+' NUMBER
和 signed_number '-' NUMBER
形式是用于表示 復數;它們要求左邊是一個(gè)實(shí)數而右邊是一個(gè)虛數。 例如 3 + 4j
。
簡(jiǎn)而言之, LITERAL
只會(huì )在 <subject> == LITERAL
時(shí)匹配成功。對于單例 None
、 True
和 False
,會(huì )使用 is
運算符。
8.6.4.4. 捕獲模式?
捕獲模式將主詞值與一個(gè)名稱(chēng)綁定。語(yǔ)法:
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í)無(wú)效的,但 case [x] | x: ...
是被允許的。
捕獲模式總是能匹配成功。綁定遵循 PEP 572 中賦值表達式運算符設立的作用域規則;名字在最接近的包含函數作用域內成為一個(gè)局部變量,除非有適用的 global
或 nonlocal
語(yǔ)句。
簡(jiǎn)而言之, NAME
總是會(huì )匹配成功且將設置 NAME = <subject>
。
8.6.4.5. 通配符模式?
通配符模式總是會(huì )匹配成功(匹配任何內容)并且不綁定任何名稱(chēng)。語(yǔ)法:
wildcard_pattern ::= '_'
在且僅在任何模式中 _
是一個(gè) 軟關(guān)鍵字。 通常情況下它是一個(gè)標識符,即使是在 match
的目標表達式、guard
和 case
代碼塊中也是如此。
簡(jiǎn)而言之,_
總是會(huì )匹配成功。
8.6.4.6. 值模式?
值模式代表 Python 中具有名稱(chēng)的值。語(yǔ)法:
value_pattern ::=attr
attr ::=name_or_attr
"." NAME name_or_attr ::=attr
| NAME
模式中帶點(diǎn)的名稱(chēng)會(huì )使用標準的 Python 名稱(chēng)解析規則 來(lái)查找。 如果找到的值與目標值比較結果相等則模式匹配成功(使用 ==
相等運算符)。
簡(jiǎn)而言之, NAME1.NAME2
僅在 <subject> == NAME1.NAME2
時(shí)匹配成功。
備注
如果相同的值在同一個(gè)匹配語(yǔ)句中出現多次,解釋器可能會(huì )緩存找到的第一個(gè)值并重新使用它,而不是重復查找。 這種緩存與特定匹配語(yǔ)句的執行嚴格掛鉤。
8.6.4.7. 組模式?
組模式允許用戶(hù)在模式周?chē)砑永ㄌ?,以強調預期的分組。 除此之外,它沒(méi)有額外的語(yǔ)法。語(yǔ)法:
group_pattern ::= "(" pattern
")"
簡(jiǎn)單來(lái)說(shuō) (P)
具有與 P
相同的效果。
8.6.4.8. 序列模式?
一個(gè)序列模式包含數個(gè)將與序列元素進(jìn)行匹配的子模式。其語(yǔ)法類(lèi)似于列表或元組的解包。
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
)
序列模式中使用圓括號或方括號沒(méi)有區別(例如 (...)
和 [...]
)。
備注
用圓括號括起來(lái)且沒(méi)有跟隨逗號的單個(gè)模式 (例如 (3 | 4)
) 是一個(gè) 分組模式。 而用方括號括起來(lái)的單個(gè)模式 (例如 [3 | 4]
) 則仍是一個(gè)序列模式。
一個(gè)序列模式中最多可以有一個(gè)星號子模式。星號子模式可以出現在任何位置。如果沒(méi)有星號子模式,該序列模式是固定長(cháng)度的序列模式;否則,其是一個(gè)可變長(cháng)度的序列模式。
下面是將一個(gè)序列模式與一個(gè)主詞值相匹配的邏輯流程:
如果主詞值不是一個(gè)序列 2 ,該序列模式匹配失敗。
如果主詞值是
str
、bytes
或bytearray
的實(shí)例,則該序列模式匹配失敗。隨后的步驟取決于序列模式是固定長(cháng)度還是可變長(cháng)度的。
如果序列模式是固定長(cháng)度的:
如果主詞序列的長(cháng)度與子模式的數量不相等,則該序列模式匹配失敗
序列模式中的子模式與主詞序列中的相應項目從左到右進(jìn)行匹配。 一旦一個(gè)子模式匹配失敗,就停止匹配。 如果所有的子模式都成功地與它們的對應項相匹配,那么該序列模式就匹配成功了。
否則,如果序列模式是變長(cháng)的:
如果主詞序列的長(cháng)度小于非星號子模式的數量,則該序列模式匹配失敗。
與固定長(cháng)度的序列一樣,靠前的非星形子模式與其相應的項目進(jìn)行匹配。
如果上一步成功,星號子模式與剩余的主詞項形成的列表相匹配,不包括星號子模式之后的非星號子模式所對應的剩余項。
剩余的非星號子模式將與相應的主詞項匹配,就像固定長(cháng)度的序列一樣。
簡(jiǎn)而言之, [P1, P2, P3,
... , P<N>]
僅在滿(mǎn)足以下情況時(shí)匹配成功:
檢查
<subject>
是一個(gè)序列len(subject) == <N>
將
P1
與<subject>[0]
進(jìn)行匹配(請注意此匹配可以綁定名稱(chēng))將
P2
與<subject>[1]
進(jìn)行匹配(請注意此匹配可以綁定名稱(chēng))…… 剩余對應的模式/元素也以此類(lèi)推。
8.6.4.9. 映射模式?
映射模式包含一個(gè)或多個(gè)鍵值模式。其語(yǔ)法類(lèi)似于字典的構造。語(yǔ)法:
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è)雙星號模式。雙星號模式必須是映射模式中的最后一個(gè)子模式。
映射模式中不允許出現重復的鍵。重復的字面值鍵會(huì )引發(fā) SyntaxError
。若是兩個(gè)鍵有相同的值將會(huì )在運行時(shí)引發(fā) ValueError
。
以下是映射模式與主詞值匹配的邏輯流程:
如果主詞值不是一個(gè)映射 3,則映射模式匹配失敗。
若映射模式中給出的每個(gè)鍵都存在于主詞映射中,且每個(gè)鍵的模式都與主詞映射的相應項匹配成功,則該映射模式匹配成功。
如果在映射模式中檢測到重復的鍵,該模式將被視作無(wú)效。對于重復的字面值,會(huì )引發(fā)
SyntaxError
;對于相同值的命名鍵,會(huì )引發(fā)ValueError
。
備注
鍵值對使用映射主詞的 get()
方法的雙參數形式進(jìn)行匹配。匹配的鍵值對必須已經(jīng)存在于映射中,而不是通過(guò) __missing__()
或 __getitem__()
即時(shí)創(chuàng )建。
簡(jiǎn)而言之, {KEY1: P1, KEY2: P2, ... }
僅在滿(mǎn)足以下情況時(shí)匹配成功:
檢查
<subject>
是映射KEY1 in <subject>
P1
與<subject>[KEY1]
相匹配…… 剩余對應的鍵/模式對也以此類(lèi)推。
8.6.4.10. 類(lèi)模式?
類(lèi)模式表示一個(gè)類(lèi)以及它的位置參數和關(guān)鍵字參數(如果有的話(huà))。語(yǔ)法:
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)鍵詞不應該在類(lèi)模式中重復出現。
The following is the logical flow for matching a class pattern against a subject value:
如果主詞值不是
name_or_attr
的實(shí)例(通過(guò)isinstance()
測試),該類(lèi)模式匹配失敗。如果沒(méi)有模式參數存在,則該模式匹配成功。 否則,后面的步驟取決于是否有關(guān)鍵字或位置參數模式存在。
對于一些內置的類(lèi)型(將在后文詳述),接受一個(gè)位置子模式,它將與整個(gè)主詞值相匹配;對于這些類(lèi)型,關(guān)鍵字模式也像其他類(lèi)型一樣工作。
如果只存在關(guān)鍵詞模式,它們將被逐一處理,如下所示:
一. 該關(guān)鍵詞被視作主體的一個(gè)屬性進(jìn)行查找。
如果這引發(fā)了除
AttributeError
以外的異常,該異常會(huì )被拋出。如果這引發(fā)了
AttributeError
,該類(lèi)模式匹配失敗。否則,與關(guān)鍵詞模式相關(guān)的子模式將與主詞的屬性值進(jìn)行匹配。 如果失敗,則類(lèi)模式匹配失??;如果成功,則繼續對下一個(gè)關(guān)鍵詞進(jìn)行匹配。
二. 如果所有的關(guān)鍵詞模式匹配成功,該類(lèi)模式匹配成功。
如果存在位置模式,在匹配前會(huì )用類(lèi)
name_or_attr
的__match_args__
屬性將其轉換為關(guān)鍵詞模式。I. The equivalent of
getattr(cls, "__match_args__", ())
is called.- 二. 若所有的位置模式都被轉換為關(guān)鍵詞模式,
匹配的過(guò)程就像只有關(guān)鍵詞模式一樣。
對于以下內置類(lèi)型,位置子模式的處理是不同的:
這些類(lèi)接受一個(gè)位置參數,其模式針對整個(gè)對象而不是某個(gè)屬性進(jìn)行匹配。例如,
int(0|1)
匹配值0
,但不匹配值0.0
或False
。
簡(jiǎn)而言之, CLS(P1, attr=P2)
僅在滿(mǎn)足以下情況時(shí)匹配成功:
isinstance(<subject>, CLS)
用
CLS.__match_args__
將P1
轉換為關(guān)鍵詞模式- 對于每個(gè)關(guān)鍵詞參數
attr=P2
: hasattr(<subject>, "attr")
將
P2
與<subject>.attr
進(jìn)行匹配
- 對于每個(gè)關(guān)鍵詞參數
…… 剩余對應的關(guān)鍵字參數/模式對也以此類(lèi)推。
8.7. 函數定義?
函數定義就是對用戶(hù)自定義函數的定義(參見(jiàn) 標準類(lèi)型層級結構 一節):
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
函數定義是一條可執行語(yǔ)句。 它執行時(shí)會(huì )在當前局部命名空間中將函數名稱(chēng)綁定到一個(gè)函數對象(函數可執行代碼的包裝器)。 這個(gè)函數對象包含對當前全局命名空間的引用,作為函數被調用時(shí)所使用的全局命名空間。
函數定義并不會(huì )執行函數體;只有當函數被調用時(shí)才會(huì )執行此操作。 4
一個(gè)函數定義可以被一個(gè)或多個(gè) decorator 表達式所包裝。 當函數被定義時(shí)將在包含該函數定義的作用域中對裝飾器表達式求值。 求值結果必須是一個(gè)可調用對象,它會(huì )以該函數對象作為唯一參數被發(fā)起調用。 其返回值將被綁定到函數名稱(chēng)而非函數對象。 多個(gè)裝飾器會(huì )以嵌套方式被應用。 例如以下代碼
@f1(arg)
@f2
def func(): pass
大致等價(jià)于
def func(): pass
func = f1(arg)(f2(func))
不同之處在于原始函數并不會(huì )被臨時(shí)綁定到名稱(chēng) func
。
在 3.9 版更改: Functions may be decorated with any valid
assignment_expression
. Previously, the grammar was
much more restrictive; see PEP 614 for details.
當一個(gè)或多個(gè) 形參 具有 形參 =
表達式 這樣的形式時(shí),該函數就被稱(chēng)為具有“默認形參值”。 對于一個(gè)具有默認值的形參,其對應的 argument 可以在調用中被省略,在此情況下會(huì )用形參的默認值來(lái)替代。 如果一個(gè)形參具有默認值,后續所有在 "*
" 之前的形參也必須具有默認值 --- 這個(gè)句法限制并未在語(yǔ)法中明確表達。
默認形參值會(huì )在執行函數定義時(shí)按從左至右的順序被求值。 這意味著(zhù)當函數被定義時(shí)將對表達式求值一次,相同的“預計算”值將在每次調用時(shí)被使用。 這一點(diǎn)在默認形參為可變對象,例如列表或字典的時(shí)候尤其需要重點(diǎn)理解:如果函數修改了該對象(例如向列表添加了一項),則實(shí)際上默認值也會(huì )被修改。 這通常不是人們所想要的。 繞過(guò)此問(wèn)題的一個(gè)方法是使用 None
作為默認值,并在函數體中顯式地對其進(jìn)測試,例如:
def whats_on_the_telly(penguin=None):
if penguin is None:
penguin = []
penguin.append("property of the zoo")
return penguin
函數調用的語(yǔ)義在 調用 一節中有更詳細的描述。 函數調用總是會(huì )給形參列表中列出的所有形參賦值,或是用位置參數,或是用關(guān)鍵字參數,或是用默認值。 如果存在 "*identifier
" 這樣的形式,它會(huì )被初始化為一個(gè)元組來(lái)接收任何額外的位置參數,默認為一個(gè)空元組。 如果存在 "**identifier
" 這樣的形式,它會(huì )被初始化為一個(gè)新的有序映射來(lái)接收任何額外的關(guān)鍵字參數,默認為一個(gè)相同類(lèi)型的空映射。 在 "*
" 或 "*identifier
" 之后的形參都是僅限關(guān)鍵字形參因而只能通過(guò)關(guān)鍵字參數傳入。 在 "/
" 之前的形參都是僅限位置形參因而只能通過(guò)位置參數傳入。
在 3.8 版更改: 可以使用 /
函數形參語(yǔ)法來(lái)標示僅限位置形參。 請參閱 PEP 570 了解詳情。
形參可以帶有 標注,其形式為在形參名稱(chēng)后加上 ": expression
"。 任何形參都可以帶有標注,甚至 *identifier
或 **identifier
這樣的形參也可以。 函數可以帶有“返回”標注,其形式為在形參列表后加上 "-> expression
"。 這些標注可以是任何有效的 Python 表達式。 標注的存在不會(huì )改變函數的語(yǔ)義。 標注值可以作為函數對象的 __annotations__
屬性中以對應形參名稱(chēng)為鍵的字典值被訪(fǎng)問(wèn)。 如果使用了 annotations
import from __future__
的方式,則標注會(huì )在運行時(shí)保存為字符串以啟用延遲求值特性。 否則,它們會(huì )在執行函數定義時(shí)被求值。 在這種情況下,標注的求值順序可能與它們在源代碼中出現的順序不同。
創(chuàng )建匿名函數(未綁定到一個(gè)名稱(chēng)的函數)以便立即在表達式中使用也是可能的。 這需要使用 lambda 表達式,具體描述見(jiàn) lambda 表達式 一節。 請注意 lambda 只是簡(jiǎn)單函數定義的一種簡(jiǎn)化寫(xiě)法;在 "def
" 語(yǔ)句中定義的函數也可以像用 lambda 表達式定義的函數一樣被傳遞或賦值給其他名稱(chēng)。 "def
" 形式實(shí)際上更為強大,因為它允許執行多條語(yǔ)句和使用標注。
程序員注意事項: 函數屬于一類(lèi)對象。 在一個(gè)函數內部執行的 "def
" 語(yǔ)句會(huì )定義一個(gè)局部函數并可被返回或傳遞。 在嵌套函數中使用的自由變量可以訪(fǎng)問(wèn)包含該 def 語(yǔ)句的函數的局部變量。 詳情參見(jiàn) 命名與綁定 一節。
8.8. 類(lèi)定義?
類(lèi)定義就是對類(lèi)對象的定義 (參見(jiàn) 標準類(lèi)型層級結構 一節):
classdef ::= [decorators
] "class"classname
[inheritance
] ":"suite
inheritance ::= "(" [argument_list
] ")" classname ::=identifier
類(lèi)定義是一條可執行語(yǔ)句。 其中繼承列表通常給出基類(lèi)的列表 (進(jìn)階用法請參見(jiàn) 元類(lèi)),列表中的每一項都應當被求值為一個(gè)允許子類(lèi)的類(lèi)對象。 沒(méi)有繼承列表的類(lèi)默認繼承自基類(lèi) object
;因此,:
class Foo:
pass
等價(jià)于
class Foo(object):
pass
隨后類(lèi)體將在一個(gè)新的執行幀 (參見(jiàn) 命名與綁定) 中被執行,使用新創(chuàng )建的局部命名空間和原有的全局命名空間。 (通常,類(lèi)體主要包含函數定義。) 當類(lèi)體結束執行時(shí),其執行幀將被丟棄而其局部命名空間會(huì )被保存。 5 一個(gè)類(lèi)對象隨后會(huì )被創(chuàng )建,其基類(lèi)使用給定的繼承列表,屬性字典使用保存的局部命名空間。 類(lèi)名稱(chēng)將在原有的全局命名空間中綁定到該類(lèi)對象。
在類(lèi)體內定義的屬性的順序保存在新類(lèi)的 __dict__
中。 請注意此順序的可靠性只限于類(lèi)剛被創(chuàng )建時(shí),并且只適用于使用定義語(yǔ)法所定義的類(lèi)。
類(lèi)的創(chuàng )建可使用 元類(lèi) 進(jìn)行重度定制。
類(lèi)也可以被裝飾:就像裝飾函數一樣,:
@f1(arg)
@f2
class Foo: pass
大致等價(jià)于
class Foo: pass
Foo = f1(arg)(f2(Foo))
裝飾器表達式的求值規則與函數裝飾器相同。 結果隨后會(huì )被綁定到類(lèi)名稱(chēng)。
在 3.9 版更改: Classes may be decorated with any valid
assignment_expression
. Previously, the grammar was
much more restrictive; see PEP 614 for details.
程序員注意事項: 在類(lèi)定義內定義的變量是類(lèi)屬性;它們將被類(lèi)實(shí)例所共享。 實(shí)例屬性可通過(guò) self.name = value
在方法中設定。 類(lèi)和實(shí)例屬性均可通過(guò) "self.name
" 表示法來(lái)訪(fǎng)問(wèn),當通過(guò)此方式訪(fǎng)問(wèn)時(shí)實(shí)例屬性會(huì )隱藏同名的類(lèi)屬性。 類(lèi)屬性可被用作實(shí)例屬性的默認值,但在此場(chǎng)景下使用可變值可能導致未預期的結果。 可以使用 描述器 來(lái)創(chuàng )建具有不同實(shí)現細節的實(shí)例變量。
8.9. 協(xié)程?
3.5 新版功能.
8.9.1. 協(xié)程函數定義?
async_funcdef ::= [decorators
] "async" "def"funcname
"(" [parameter_list
] ")" ["->"expression
] ":"suite
Python 協(xié)程的執行可以在多個(gè)位置上被掛起和恢復 (參見(jiàn) coroutine)。 await
表達式,async for
以及 async with
只能在協(xié)程函數體中使用。
使用 async def
語(yǔ)法定義的函數總是為協(xié)程函數,即使它們不包含 await
或 async
關(guān)鍵字。
在協(xié)程函數體中使用 yield from
表達式將引發(fā) SyntaxError
。
協(xié)程函數的例子:
async def func(param1, param2):
do_stuff()
await some_coroutine()
在 3.7 版更改: await
和 async
現在是保留關(guān)鍵字;在之前版本中它們僅在協(xié)程函數內被當作保留關(guān)鍵字。
8.9.2. async for
語(yǔ)句?
async_for_stmt ::= "async" for_stmt
asynchronous iterable 提供了 __aiter__
方法,該方法會(huì )直接返回 asynchronous iterator,它可以在其 __anext__
方法中調用異步代碼。
async for
語(yǔ)句允許方便地對異步可迭代對象進(jìn)行迭代。
以下代碼:
async for TARGET in ITER:
SUITE
else:
SUITE2
在語(yǔ)義上等價(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
另請參閱 __aiter__()
和 __anext__()
了解詳情。
在協(xié)程函數體之外使用 async for
語(yǔ)句將引發(fā) SyntaxError
。
8.9.3. async with
語(yǔ)句?
async_with_stmt ::= "async" with_stmt
asynchronous context manager 是一種 context manager,能夠在其 enter 和 exit 方法中暫停執行。
以下代碼:
async with EXPRESSION as TARGET:
SUITE
在語(yǔ)義上等價(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)
另請參閱 __aenter__()
和 __aexit__()
了解詳情。
在協(xié)程函數體之外使用 async with
語(yǔ)句將引發(fā) SyntaxError
。
參見(jiàn)
- PEP 492 - 使用 async 和 await 語(yǔ)法實(shí)現協(xié)程
將協(xié)程作為 Python 中的一個(gè)正式的單獨概念,并增加相應的支持語(yǔ)法。
備注
- 1
異常會(huì )被傳播給發(fā)起調用棧,除非存在一個(gè)
finally
子句正好引發(fā)了另一個(gè)異常。 新引發(fā)的異常將導致舊異常的丟失。- 2
在模式匹配中,序列被定義為以下幾種之一:
繼承自
collections.abc.Sequence
的類(lèi)注冊為
collections.abc.Sequence
的 Python 類(lèi)設置了 (CPython)
Py_TPFLAGS_SEQUENCE
位的內置類(lèi)繼承自上述任何一個(gè)類(lèi)的類(lèi)
下列標準庫中的類(lèi)都是序列:
備注
類(lèi)型為
str
,bytes
和bytearray
的目標值不能匹配序列模式。- 3
在模式匹配中,映射被定義為以下幾種之一:
繼承自
collections.abc.Mapping
的類(lèi)注冊為
collections.abc.Mapping
的 Python 類(lèi)設置了 (CPython)
Py_TPFLAGS_MAPPING
位的內置類(lèi)繼承自上述任何一個(gè)類(lèi)的類(lèi)
標準庫中的
dict
和types.MappingProxyType
類(lèi)都屬于映射。- 4
作為函數體的第一條語(yǔ)句出現的字符串字面值會(huì )被轉換為函數的
__doc__
屬性,也就是該函數的 docstring。- 5
作為類(lèi)體的第一條語(yǔ)句出現的字符串字面值會(huì )被轉換為命名空間的
__doc__
條目,也就是該類(lèi)的 docstring。