### 7. 復合語句
復合語句包含(多組)其它語句;它們以某種方式影響或者控制其它那些語句的執行。通常,復合語句跨越多行,雖然一條完整的復合語句可以用簡潔的形式包含在一行之中。
[if](#)、[while](#)和[for](#)語句實現傳統的控制流句法結構。[try](#)指出一組語句的異常處理器和/或清理代碼。函數和類定義在語法上同樣也是復合語句。
復合語句由一個或多個‘子句’組成。一條子句由語句首和‘語句組’組成。一條特定的復合語句的所有子句的語句首都處在相同的縮進水平上。每一個子句的語句首以一個唯一的標識關鍵字開始并以冒號結束。語句組是由一條子句控制的一組語句。一個語句組可以是語句首冒號之后的同一行上緊跟一個或多個分號分隔的簡單語句,也可以是后續行上一個或多個縮進的語句。只有后一種形式的語句組可以包含嵌套的復合語句;下面的語句是非法的,最主要是因為不能明確隨后的[else](#)子句屬于哪一個[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
| funcdef
| classdef
| decorated
suite ::= stmt_list NEWLINE | NEWLINE INDENT statement+ DEDENT
statement ::= stmt_list NEWLINE | compound_stmt
stmt_list ::= simple_stmt (";" simple_stmt)* [";"]
~~~
注意語句永遠以NEWLINE結束,其后可能跟隨一個DEDENT。還要注意可選的續行子句永遠以一個不能作為語句開始的關鍵字開始,因此不會有歧義(‘懸掛的[else](#)’問題在Python中通過要求嵌套的[if](#)語句必須縮進得到解決)。
為了清晰起見,下面小節中的語法規則的格式會將子句放在單獨的一行。
### 7.1. [if](#) 語句
[if](#) 語句用于條件執行:
~~~
if_stmt ::= "if" expression ":" suite
( "elif" expression ":" suite )*
["else" ":" suite]
~~~
它通過對表達式逐個求值直到其中一個為真的方式準確地選擇一個語句組(真和假的定義參見[*布爾操作*](#) 一節);然后執行這個語句組([if](#)語句的其它部分不會被執行或求值)。如果所有表達式都為假,則執行[else](#)子句的語句組。
### 7.2.?[while](#) 語句
[while](#)語句用于重復執行直到某個表達式為真:
~~~
while_stmt ::= "while" expression ":" suite
["else" ":" suite]
~~~
它重復測試表達式,如果為真,則執行第一個語句組;如果表達式為假(可能是第一次測試),則執行[else](#)子句且終止循環。
第一個語句組中執行的[break](#)語句會終止循環而不執行[else](#)子句的語句組。在第一個語句組中執行的[continue](#)語句會跳過語句組中剩余的語句并返回繼續測試表達式。
### 7.3. [for](#) 語句
[for](#)語句用于迭代一個序列的元素(例如字符串、元組或者列表)或者其它可迭代的對象:
~~~
for_stmt ::= "for" target_list "in" expression_list ":" suite
["else" ":" suite]
~~~
表達式列表值計算一次;它應當產生一個可迭代的對象。expression_list的結果創建一個迭代器。然后以索引的升序為順序,為迭代器提供的每個元素執行一次語句組。每個元素使用標準的賦值規則被賦值給目標列表,然后執行語句組。當元素取盡時(如果序列為空則立刻取盡),則執行[else](#)子句中的語句組并終止循環。
第一個語句組中的[break](#)語句會終止循環而不執行[else](#)子句的語句組。在第一個語句組中執行的[continue](#)語句會跳過語句組的剩余語句并繼續下一個元素,如果沒有下一個元素則繼續執行[else](#)子句。
語句組可以給目標列表中的變量賦值;這不影響下一個賦值給它的元素。
當循環結束時目標序列不會被刪除,但是如果序列為空,循環不會賦任何值給它。提示:內建的[range()](# "range")函數返回整數的序列,它適用于模擬Pascal 語言中fori:=atobdo效果;例如,range(3) 返回列表[0,1,2]。
注意
當序列被循環修改時,會發生微妙的事情(只有可變類型的序列會發生,例如列表)。有一個內部計數器用于跟蹤下一輪循環使用哪一個元素,并且每次迭代中會增加。當計數器達到序列的長度時循環終止。這意味著如果語句組從序列中刪除當前(或者前面的)元素,下一個元素將被跳過(因為它獲取當前已經被處理過的元素的索引)。同樣地,如果語句組在序列中當前元素之前插入一個元素,那么當前元素將在下一次循環被再次處理。這可能導致難以覺察的錯誤,但可以通過使用整個序列的切片生成臨時拷貝避免這個問題,例如,
~~~
for x in a[:]:
if x < 0: a.remove(x)
~~~
### 7.4.?[try](#) 語句
[try](#)語句為一組語句指定異常處理器和/或清理代碼:
~~~
try_stmt ::= try1_stmt | try2_stmt
try1_stmt ::= "try" ":" suite
("except" [expression [("as" | ",") target]] ":" suite)+
["else" ":" suite]
["finally" ":" suite]
try2_stmt ::= "try" ":" suite
"finally" ":" suite
~~~
版本2.5 中的新變化:在以前版本的Python 中,[try](#)...[except](#)...[finally](#) 不工作。[try](#)...[except](#)必須嵌套在[try](#)...[finally](#)中。
[except](#)子句指定一個或多個異常處理器.。當[try](#)子句中沒有出現異常時,不會執行異常處理器。當[try](#)語句組中出現異常時,開始搜索異常處理器。搜索依次檢查異常子句直到找到與異常匹配的一個。沒有表達式的異常子句,如果出現,必須放在最后;它匹配任何異常。對于一個帶有表達式的異常子句,該表達式將被求值,如果結果對象與異常“兼容”,則認為子句與異常匹配。對象與異常兼容,如果它是異常對象的類或基類或者是一個包含與異常兼容的元素的元組。
如果沒有except子句匹配到異常,異常處理器的搜索將繼續在外層代碼和調用棧上進行。[[1]](#)
如果計算except子句頭部的一個表達式引發了異常, 那么就會中斷原異常處理器的搜索, 而在外層代碼和調用棧上搜索新的異常處理器(就好像是整個[try](#)語句發生了異常一樣)。
當找到一個匹配的except子句時,異常就被賦給excep子句中的目標,然后執行excep子句的語句組。所有的異常子句必須具有一個可執行的代碼塊。當到達該代碼塊的結尾時,在真個try語句之后執行正常繼續。(這意味著如果同一個異常存在兩個嵌套的處理器,且異常發生在內部處理器的try子句中,那么外邊的處理器不會處理這個異常。)
在執行except子句的語句組之前,異常的詳細信息被賦值給[sys](# "sys: Access system-specific parameters and functions.")模塊中的三個變量:sys.exc_type接收標識異常的對象;sys.exc_value接收異常的參數;sys.exc_traceback接收一個回溯對象(參見[*標準類型的層級*](#)一節)指示程序中異常發生的點。這些詳細信息也可以通過[sys.exc_info()](# "sys.exc_info")函數得到,它返回一個元組(exc_type,exc_value,exc_traceback)。鼓勵使用這個函數而不鼓勵使用對應的變量,因為在多線程程序中它們的使用是不安全的。從Python 1.5 開始,這些值會在處理異常的函數返回時會恢復它們之前的值(調用之前的值)。
如果控制流從[try](#)子句的結尾出來時,則執行可選的[else](#)子句。[[2]](#)[else](#)子句中的異常不會被前面的[except](#)子句處理。
如果有[finally](#)出現,它指定一個“清除”處理器。首先執行[try](#)子句被執行,然后包括任何[except](#)和[else](#)子句。如果異常發生在任何子句中且沒有被處理,那么異常會被臨時保存起來。最后執行[finally](#)子句。如果有保存的異常,它會在[finally](#)子句結束時被重新拋出。如果[finally](#)拋出另外一個異常或者執行一個[return](#)或[break](#)語句,那么保存的異常會被丟棄:
~~~
>>> def f():
... try:
... 1/0
... finally:
... return 42
...
>>> f()
42
~~~
在[finally](#)子句執行過程中程序訪問不到異常信息。
當[return](#)、[break](#)或[continue](#)語句在[try](#)...[finally](#)語句的[try](#)語句組中被執行,[finally](#)子句在‘出口’處同樣被執行。[continue](#)語句出現在[finally](#)子句中是非法的。(原因是當前實現的問題?— 該限制在未來可能會去掉)。
函數的返回值取決于執行的最后一條[return](#)語句。因為[finally](#)子句會永遠執行,在[finally](#)子句中執行的[return](#)語句將永遠是最后執行的一條語句:
~~~
>>> def foo():
... try:
... return 'try'
... finally:
... return 'finally'
...
>>> foo()
'finally'
~~~
額外的信息可以在[*異常*](#)一節中找到,關于如何使用[raise](#)語句產生異常可以在[*raise語句*](#)一節中找到。
### 7.5. [with](#)?語句
出現于版本2.5。
[with](#)用于和上下文管理器定義的方法一起封裝代碼塊的執行(參見[*With語句的上下文管理器*](#)一節)。這允許常見的[try](#)...[except](#)...[finally](#)的用法模式封裝起來以方便地重用。
~~~
with_stmt ::= "with" with_item ("," with_item)* ":" suite
with_item ::= expression ["as" target]
~~~
帶有一個“item”的[with](#)語句的執行按下面的方式進行:
1.
計算上下文表達式([with_item](#)中給出的表達式)以獲得一個上下文管理器。
1.
加載上下文管理器的[__exit__()](# "object.__exit__")方法留著后面使用。
1.
調用上下文管理器的[__enter__()](# "object.__enter__")方法。
1.
如果[with](#)語句包含一個目標,[__enter__()](# "object.__enter__")的返回值將賦值給它。
注意
[with](#)語句保證如果[__enter__()](# "object.__enter__")方法沒有錯誤返回,那么[__exit__()](# "object.__exit__")將永遠被調用。因此,如果在給目標列表賦值過程中出現錯誤,它將被與語句組中發生的錯誤同樣對待。參見下面的第6步。
1.
執行語句組。
1.
調用上下文管理器的[__exit__()](# "object.__exit__")方法。如果異常導致語句組要退出,那么異常的類型、值和回溯棧被當做參數傳遞給[__exit__()](# "object.__exit__")。否則,提供三個[None](# "None") 參數。
如果語句組由于異常退出,且[__exit__()](# "object.__exit__")方法的返回值為假,異常將被重新引發。如果返回值為真,異常將被取消,并繼續執行[with](#)語句之后的語句。
如果語句組由于異常以外的其它任何原因退出,[__exit__()](# "object.__exit__")的返回值將被忽略,執行將在退出發生的正常位置繼續。
如果有多個條目,上下文管理器的處理如同嵌套的多個[with](#)語句:
~~~
with A() as a, B() as b:
suite
~~~
等同于
~~~
with A() as a:
with B() as b:
suite
~~~
注意
在Python 2.5中,[with](#)只有在with_statement特性被啟用的時候才允許使用。在Python 2.6中,它被永遠啟用。
版本2.7 中的新變化:支持多個上下文表達式。
另請參閱
[**PEP 0343**](http://www.python.org/dev/peps/pep-0343) - “with”語句Python with語句的說明、背景和實例。
### 7.6. 函數定義
函數定義定義一個用戶自定義的函數對象(參見[*標準類型的層次*](#)一節):
~~~
decorated ::= decorators (classdef | funcdef)
decorators ::= decorator+
decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE
funcdef ::= "def" funcname "(" [parameter_list] ")" ":" suite
dotted_name ::= identifier ("." identifier)*
parameter_list ::= (defparameter ",")*
( "*" identifier ["," "**" identifier]
| "**" identifier
| defparameter [","] )
defparameter ::= parameter ["=" expression]
sublist ::= parameter ("," parameter)* [","]
parameter ::= identifier | "(" sublist ")"
funcname ::= identifier
~~~
函數定義是一個可執行的語句。它的執行將綁定當前局部命名空間中的函數名到一個函數對象(函數可執行代碼的封裝)。函數對象包含一個對當前全局命名空間的引用,作為函數調用時使用的全局命名空間。
函數定義不會執行函數體;它只有在調用函數的時候才執行。[[3]](#)
函數定義可能被一個或多個[*修飾符*](#)表達式封裝。修飾符表達式在函數定義時于包含函數定義的定義域中求值。求值的結果必須是一個可調用對象,它以該函數對象為唯一的參數。調用的返回值綁定在函數名而不是函數對象上。多個修飾符是以嵌套的方式作用的。例如,下面的代碼:
~~~
@f1(arg)
@f2
def func(): pass
~~~
等同于:
~~~
def func(): pass
func = f1(arg)(f2(func))
~~~
當一個或多個最上層的[*參數*](#)具有*parameter*=*expression*的形式時,稱該函數具有“默認參數值。”對于具有默認值的參數,對應的[*參數*](#)在調用時可以省略,在這種情況下使用參數的默認值。如果一個參數具有默認值,所有隨后的參數也必須具有默認值?— 這個限制在語法中沒有表達出來的。
**默認的參數值在執行函數定義是求值。**這意味著只在函數定義的時候該表達式求一次值,以后每次調用使用相同的“提前計算好的”值。這對于理解默認參數是可變對象時特別重要,例如列表或字典:如果函數修改了該對象(例如,向列表添加一個元素),默認值將受影響被修改。這通常不是想要的。一種變通的方法是使用None作為默認值,然后在函數體中明確地測試它,例如:
~~~
def whats_on_the_telly(penguin=None):
if penguin is None:
penguin = []
penguin.append("property of the zoo")
return penguin
~~~
函數調用的語義在[*調用*](#)一節有更詳細的描述。函數調用永遠會給參數列表中的所有的參數賦值,無論是位置參數還是關鍵字參數或默認值。如果出現“*identifier”的形式,那么它被初始化為一個可以接收任意多余位置參數元組,默認為空元組。如果有“**identifier”的形式,那么它被初識化為一個可以接收任意的多余關鍵字參數的新的字典,默認值為空字典。
也可以創建匿名函數(沒有綁定到某個名稱的函數),以在表達式中直接使用。它使用lambda 表達式,在[*Lambdas*](#)一節中有詳細描述。注意lambda 表達式僅僅是簡單的函數定義的簡寫;“[def](#)”語句中定義的函數和lambda 表達式定義的函數一樣,可以傳遞或者賦值給另外一個名稱。“[def](#)” 形式事實上功能更強大,因為它允許執行多條語句。
**程序員的注意事項:**函數是第一級的對象。在函數內部執行的“def”形式定義一個可以被返回或傳遞的局部函數。在嵌套的函數中使用的自由變量可以訪問包含該def的函數的局部變量。詳細信息參見[*名稱和綁定*](#)一節。
### 7.7. 類定義
類定義定義一個類對象(參見[*標準類型的層次*](#) 一節):
~~~
classdef ::= "class" classname [inheritance] ":" suite
inheritance ::= "(" [expression_list] ")"
classname ::= identifier
~~~
類定義是一個可執行語句。它首先計算inheritance序列,如果存在的話。inheritance序列中的每一項都應該是一個類對象或者允許生成子類的類類型。然后使用一個新創建的局部命名空間和初始的全局命名空間,在新的執行幀中執行類的語句組(參見[*名稱和綁定*](#)一節)。(通常,語句組只包含函數的定義。)當類的語句組結束執行,它的執行幀被丟棄但是局部命名空間被保存下來。[[4]](#)最后使用inheritance序列作為基類創建一個類對象,并保存局部命名空間作為屬性字典。類的名稱被綁定到初識局部命名空間中類對象。
**程序員的注意事項:**類定義中定義的變量是類變量;它們被所有的實例共享。要創建實例變量,可以在方法中使用self.name=value設置它們。類變量和實例變量都可以通過“self.name”記號訪問,當用相同的方式訪問時實例變量將隱藏相同名稱的類變量。類變量可以作為實例變量的默認值使用,但是這種用法如果使用可變的值可能導致意想不到的結果。對于[*新式類*](#),可以使用描述符創建具有不同實現細節的實例變量。
類定義,類似于函數定義,可以被一個或多個[*描述符*](#) 表達式封裝。描述符表達式的計算規則和函數相同。結果必須是一個類對象,并綁定于類的名字。
腳注
| [[1]](#) | 異常將擴散到調用棧除非[finally](#)子句碰巧引發另外一個異常。這個新的異常導致舊的異常丟失。 |
|-----|-----|
| [[2]](#) | 目前,控制“從末尾流出”除了下面這些情況:異常或執行[return](#)、[continue](#)、[break](#)語句。 |
|-----|-----|
| [[3]](#) | 作為函數體第一條語句出現的字符串字面值被轉換成函數的__doc__屬性,即函數的[*文檔字符串*](#)。 |
|-----|-----|
| [[4]](#) | 作為類體的第一條語句出現的語句被轉換為該命名空間的__doc__屬性,即類的[*文檔字符串*](#)。 |
|-----|-----|
- Python 2 教程
- 1. 吊吊你的胃口
- 2. Python 解釋器
- 3. Python簡介
- 4. 控制流
- 5. 數據結構
- 6. 模塊
- 7. 輸入和輸出
- 8. 錯誤和異常
- 9. 類
- 10. 標準庫概覽
- 11. 標準庫概覽 — 第II部分
- 12.現在怎么辦?
- 13. 交互式輸入的編輯和歷史記錄
- 14. 浮點數運算:問題和局限
- Python 2 標準庫
- 1. 引言
- 2. 內建函數
- 3. 不太重要的內建函數
- 4. 內建的常量
- 5. 內建的類型
- 6. 內建的異常
- 7. String Services
- 8. Data Types
- 9. Numeric and Mathematical Modules
- 10. File and Directory Access
- 11. Data Persistence
- 13. File Formats
- 14. Cryptographic Services
- 15. Generic Operating System Services
- 16. Optional Operating System Services
- 17. Interprocess Communication and Networking
- 18. Internet Data Handling
- 20. Internet Protocols and Support
- 26. Debugging and Profiling
- 28. Python Runtime Services
- Python 2 語言參考
- 1. 簡介
- 2. 詞法分析
- 3. 數據模型
- 4. 執行模型
- 5. 表達式
- 6. 簡單語句
- 7. 復合語句
- 8. 頂層的組件
- 9. 完整的語法規范
- Python 3 教程
- 1. 引言
- 2. Python 解釋器
- 3. Python簡介
- 4. 控制流
- 5. 數據結構
- 6. 模塊
- 7. 輸入和輸出
- 8. 錯誤和異常
- 9. 類
- 10. 標準庫概覽
- 11. 標準庫概覽 — 第II部分
- 12.現在怎么辦?
- 13. 交互式輸入的編輯和歷史記錄
- 14. 浮點數運算:問題和局限