7. 簡單陳述式

一個簡單陳述式會被包含在一個單獨的邏輯列中。多個簡單陳述式用分號分隔就可以出現在同一列中。簡單陳述式的語法如下:

simple_stmt: expression_stmt
             | assert_stmt
             | assignment_stmt
             | augmented_assignment_stmt
             | annotated_assignment_stmt
             | pass_stmt
             | del_stmt
             | return_stmt
             | yield_stmt
             | raise_stmt
             | break_stmt
             | continue_stmt
             | import_stmt
             | future_stmt
             | global_stmt
             | nonlocal_stmt
             | type_stmt

7.1. 運算式陳述式

運算式陳述式(主要在互動模式下)用於計算並寫入一個值,或者(通常)用於呼叫一個程序 (procedure)(一個不回傳有意義結果的函式;在 Python 中,程序會回傳 None 值)。運算式陳述式的其他用途也是被允許的,並且偶爾會很有用。運算式陳述式的語法如下:

expression_stmt: starred_expression

運算式陳述式會對運算式串列(可能是單個運算式)進行求值 (evaluate)。

在互動模式下,如果值不是 None,就會以內建的 repr() 函式轉換成字串,並將結果字串單獨寫入標準輸出的一列中(除非結果是 None,則程序呼叫就不會產生任何輸出)。

7.2. 賦值陳述式

賦值陳述式用於將名稱(重新)繫結到值,以及修改可變物件的屬性或項目:

assignment_stmt: (target_list "=")+ (starred_expression | yield_expression)
target_list:     target ("," target)* [","]
target:          identifier
                 | "(" [target_list] ")"
                 | "[" [target_list] "]"
                 | attributeref
                 | subscription
                 | "*" target

(關於 attributerefsubscription 的語法定義,請參閱 Primaries 章節。)

賦值陳述式會對運算式串列進行求值(請記住,這可以是單個運算式或以逗號分隔的串列,後者會產生一個元組),並將單個結果物件從左到右賦值給每個目標串列。

賦值是根據目標(串列)的形式遞迴定義的。當目標是可變物件的一部分(屬性參照或下標)時,該可變物件最終必須執行賦值並決定其有效性,如果賦值不被接受則可能會引發例外。各種型別遵循的規則以及引發的例外在物件型別的定義中給定(請參閱標準型別階層章節)。

將物件賦值給目標串列(可選擇性地用圓括號或方括號括起來)是以下列方式遞迴定義的。

  • 如果目標串列是單個目標且沒有尾隨逗號(可選擇性地用圓括號括起來),則物件會被賦值給該目標。

  • 否則:

    • 如果目標串列包含一個以星號為前綴的目標,稱為 "starred" 目標:該物件必須是一個可疊代物件,其項目數量至少與目標串列中的目標數量減一一樣多。可疊代物件的前幾個項目從左到右賦值給 starred 目標之前的目標。可疊代物件的最後幾個項目賦值給 starred 目標之後的目標。然後可疊代物件中剩餘項目的串列會被賦值給 starred 目標(該串列可以為空)。

    • 否則:該物件必須是一個可疊代物件,其項目數量與目標串列中的目標數量相同,並且這些項目從左到右賦值給對應的目標。

將物件賦值給單個目標是以下列方式遞迴定義的。

  • 如果目標是一個識別字(名稱):

    • 如果該名稱沒有出現在當前程式碼區塊的 globalnonlocal 陳述式中:該名稱會被繫結到當前區域命名空間中的物件。

    • 否則:該名稱分別被繫結到全域命名空間或由 nonlocal 決定的外層命名空間中的物件。

    如果名稱已經被繫結,則會重新繫結。這可能導致先前繫結到該名稱的物件的參照計數變為零,從而導致該物件被釋放並呼叫其解構函式(如果有的話)。

  • 如果目標是屬性參照:參照中的主要運算式會被求值。它應該產生一個具有可賦值屬性的物件;如果不是這種情況,則會引發 TypeError。然後要求該物件將被賦值的物件賦值給指定的屬性;如果它無法執行賦值,則會引發例外(通常是 AttributeError 但並非一定)。

    注意:如果物件是類別實例,且屬性參照出現在賦值運算子的兩側,右側運算式 a.x 可以存取實例屬性或(如果不存在實例屬性)類別屬性。左側目標 a.x 總是被設定為實例屬性,如有必要會建立它。因此,a.x 的兩次出現不一定指向同一個屬性:如果右側運算式參照的是類別屬性,左側會建立一個新的實例屬性作為賦值的目標:

    class Cls:
        x = 3             # 類別變數
    inst = Cls()
    inst.x = inst.x + 1   # 將 inst.x 寫為 4,Cls.x 保持為 3
    

    此描述不一定適用於描述器屬性,例如使用 property() 建立的特性 (property)。

  • 如果目標是下標:參照中的主要運算式會被求值。接著,下標運算式會被求值。然後會以兩個引數(下標和被賦值的物件)呼叫主要物件的 __setitem__() 方法。

    通常 __setitem__() 被定義在可變序列物件(如串列)和對映物件(如字典)上,其行為如下。

    如果主要物件是可變序列物件(如串列),下標必須產生一個整數。如果它是負數,會加上序列的長度。結果值必須是一個非負整數且小於序列的長度,然後要求該序列將被賦值的物件賦值給具有該索引的項目。如果索引超出範圍,則會引發 IndexError(對下標序列的賦值無法向串列新增項目)。

    如果主要物件是對映物件(如字典),下標的型別必須與對映的鍵型別相容,然後要求該對映建立一個將下標對映到被賦值物件的鍵/值對。這可以替換具有相同鍵值的現有鍵/值對,或插入新的鍵/值對(如果不存在相同值的鍵)。

    如果目標是切片:主要運算式應該求值為一個可變序列物件(如串列)。被賦值的物件應該是可疊代的。切片的下界和上界應該是整數;如果它們是 None(或不存在),預設值為零和序列的長度。如果任一邊界為負數,則會加上序列的長度。結果邊界會被裁剪到零和序列長度之間(包含邊界)。最後,要求該序列物件用被賦值序列的項目替換切片。切片的長度可能與被賦值序列的長度不同,因此會改變目標序列的長度(如果目標序列允許的話)。

雖然賦值的定義意味著左側和右側之間的重疊是「同時的」(例如 a, b = b, a 會交換兩個變數),但被賦值變數集合內部的重疊是從左到右發生的,有時會導致混淆。例如,以下程式會印出 [0, 2]

x = [0, 1]
i = 0
i, x[i] = 1, 2         # i 先被更新,然後 x[i] 再被更新
print(x)

也參考

PEP 3132 - 擴充可疊代物件拆解

*target 功能的規格說明。

7.2.1. 擴增賦值陳述式

擴增賦值是將二元運算與賦值陳述式結合在單一陳述式中: