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