7. 簡單語句?

簡單語句由一個單獨的邏輯行構(gòu)成。 多條簡單語句可以存在于同一行內(nèi)并以分號分隔。 簡單語句的句法為:

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

7.1. 表達式語句?

表達式語句用于計算和寫入值(大多是在交互模式下),或者(通常情況)調(diào)用一個過程 (過程就是不返回有意義結(jié)果的函數(shù);在 Python 中,過程的返回值為 None)。 表達式語句的其他使用方式也是允許且有特定用處的。 表達式語句的句法為:

expression_stmt ::=  starred_expression

表達式語句會對指定的表達式列表(也可能為單一表達式)進行求值。

在交互模式下,如果結(jié)果值不為 None,它會通過內(nèi)置的 repr() 函數(shù)轉(zhuǎn)換為一個字符串,該結(jié)果字符串將以單獨一行的形式寫入標準輸出(例外情況是如果結(jié)果為 None,則該過程調(diào)用不產(chǎn)生任何輸出。)

7.2. 賦值語句?

賦值語句用于將名稱(重)綁定到特定值,以及修改屬性或可變對象的成員項:

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

(請參閱 原型 一節(jié)了解 屬性引用, 抽取切片 的句法定義。)

賦值語句會對指定的表達式列表進行求值(注意這可能為單一表達式或是由逗號分隔的列表,后者將產(chǎn)生一個元組)并將單一結(jié)果對象從左至右逐個賦值給目標列表。

賦值是根據(jù)目標(列表)的格式遞歸地定義的。 當目標為一個可變對象(屬性引用、抽取或切片)的組成部分時,該可變對象必須最終執(zhí)行賦值并決定其有效性,如果賦值操作不可接受也可能引發(fā)異常。 各種類型可用的規(guī)則和引發(fā)的異常通過對象類型的定義給出(參見 標準類型層級結(jié)構(gòu) 一節(jié))。

對象賦值的目標對象可以包含于圓括號或方括號內(nèi),具體操作按以下方式遞歸地定義。

  • 如果目標列表為后面不帶逗號、可以包含于圓括號內(nèi)的單一目標,則將對象賦值給該目標。

  • Else:

    • 如果目標列表包含一個帶有星號前綴的目標,這稱為“加星”目標:則該對象至少必須為與目標列表項數(shù)減一相同項數(shù)的可迭代對象。 該可迭代對象前面的項將按從左至右的順序被賦值給加星目標之前的目標。 該可迭代對象末尾的項將被賦值給加星目標之后的目標。 然后該可迭代對象中剩余項的列表將被賦值給加星目標(該列表可以為空)。

    • 否則:該對象必須為具有與目標列表相同項數(shù)的可迭代對象,這些項將按從左至右的順序被賦值給對應的目標。

對象賦值給單個目標的操作按以下方式遞歸地定義。

  • 如果目標為標識符(名稱):

    • 如果該名稱未出現(xiàn)于當前代碼塊的 globalnonlocal 語句中:該名稱將被綁定到當前局部命名空間的對象。

    • 否則:該名稱將被分別綁定到全局命名空間或由 nonlocal 所確定的外層命名空間的對象。

    如果該名稱已經(jīng)被綁定則將被重新綁定。 這可能導致之前被綁定到該名稱的對象的引用計數(shù)變?yōu)榱?,造成該對象進入釋放過程并調(diào)用其析構(gòu)器(如果存在)。

  • 如果該對象為屬性引用:引用中的原型表達式會被求值。 它應該產(chǎn)生一個具有可賦值屬性的對象;否則將引發(fā) TypeError。 該對象會被要求將可賦值對象賦值給指定的屬性;如果它無法執(zhí)行賦值,則會引發(fā)異常 (通常應為 AttributeError 但并不強制要求)。

    注意:如果該對象為類實例并且屬性引用在賦值運算符的兩側(cè)都出現(xiàn),則右側(cè)表達式 a.x 可以訪問實例屬性或(如果實例屬性不存在)類屬性。 左側(cè)目標 a.x 將總是設定為實例屬性,并在必要時創(chuàng)建該實例屬性。 因此 a.x 的兩次出現(xiàn)不一定指向相同的屬性:如果右側(cè)表達式指向一個類屬性,則左側(cè)會創(chuàng)建一個新的實例屬性作為賦值的目標:

    class Cls:
        x = 3             # class variable
    inst = Cls()
    inst.x = inst.x + 1   # writes inst.x as 4 leaving Cls.x as 3
    

    此描述不一定作用于描述器屬性,例如通過 property() 創(chuàng)建的特征屬性。

  • 如果目標為一個抽取項:引用中的原型表達式會被求值。 它應當產(chǎn)生一個可變序列對象(例如列表)或一個映射對象(例如字典)。 接下來,該抽取表達式會被求值。

    如果原型為一個可變序列對象(例如列表),抽取應產(chǎn)生一個整數(shù)。 如其為負值,則再加上序列長度。 結(jié)果值必須為一個小于序列長度的非負整數(shù),序列將把被賦值對象賦值給該整數(shù)指定索引號的項。 如果索引超出范圍,將會引發(fā) IndexError (給被抽取序列賦值不能向列表添加新項)。

    如果原型為一個映射對象(例如字典),抽取必須具有與該映射的鍵類型相兼容的類型,然后映射中會創(chuàng)建一個將抽取映射到被賦值對象的鍵/值對。 這可以是替換一個現(xiàn)有鍵/值對并保持相同鍵值,也可以是插入一個新鍵/值對(如果具有相同值的鍵不存在)。

    對于用戶定義對象,會調(diào)用 __setitem__() 方法并附帶適當?shù)膮?shù)。

  • 如果目標為一個切片:引用中的原型表達式會被求值。 它應當產(chǎn)生一個可變序列對象(例如列表)。 被賦值對象應當是一個相同類型的序列對象。 接下來,下界與上界表達式如果存在的話將被求值;默認值分別為零和序列長度。 上下邊界值應當為整數(shù)。 如果某一邊界為負值,則會加上序列長度。 求出的邊界會被裁剪至介于零和序列長度的開區(qū)間中。 最后,將要求序列對象以被賦值序列的項替換該切片。 切片的長度可能與被賦值序列的長度不同,這會在目標序列允許的情況下改變目標序列的長度。

CPython implementation detail: 在當前實現(xiàn)中,目標的句法被當作與表達式的句法相同,無效的句法會在代碼生成階段被拒絕,導致不太詳細的錯誤信息。

雖然賦值的定義意味著左手邊與右手邊的重疊是“同時”進行的(例如 a, b = b, a 會交換兩個變量的值),但在賦值給變量的多項集 之內(nèi) 的重疊是從左至右進行的,這有時會令人混淆。 例如,以下程序?qū)蛴〕?[0, 2]:

x = [0, 1]
i = 0
i, x[i] = 1, 2         # i is updated, then x[i] is updated
print(x)

參見

PEP 3132 - 擴展的可迭代對象拆包

*target 特性的規(guī)范說明。

7.2.1. 增強賦值語句?

增強賦值語句就是在單個語句中將二元運算和賦值語句合為一體:

augmented_assignment_stmt ::=  augtarget augop (expression_list | yield_expression)
augtarget                 ::=  identifier | attributeref | subscription | slicing
augop                     ::=  "+=" | "-=" | "*=" | "@=" | "/=" | "http://=" | "%=" | "**="
                               | ">>=" | "<<=" | "&=" | "^=" | "|="

(請參閱 原型 一節(jié)了解最后三種符號的句法定義。)

增強賦值語句將對目標和表達式列表求值(與普通賦值語句不同的是,前者不能為可迭代對象拆包),對兩個操作數(shù)相應類型的賦值執(zhí)行指定的二元運算,并將結(jié)果賦值給原始目標。 目標僅會被求值一次。

增強賦值語句例如 x += 1 可以改寫為 x = x + 1 獲得類似但并非完全等價的效果。 在增強賦值的版本中,x 僅會被求值一次。 而且,在可能的情況下,實際的運算是 原地 執(zhí)行的,也就是說并不是創(chuàng)建一個新對象并將其賦值給目標,而是直接修改原對象。

不同于普通賦值,增強賦值會在對右手邊求值 之前 對左手邊求值。 例如,a[i] += f(x) 首先查找 a[i],然后對 f(x) 求值并執(zhí)行加法操作,最后將結(jié)果寫回到 a[i]。

除了在單個語句中賦值給元組和多個目標的例外情況,增強賦值語句的賦值操作處理方式與普通賦值相同。 類似地,除了可能存在 原地 操作行為的例外情況,增強賦值語句執(zhí)行的二元運算也與普通二元運算相同。

對于屬性引用類目標,針對常規(guī)賦值的 關于類和實例屬性的警告 也同樣適用。

7.2.2. 帶標注的賦值語句?

標注 賦值就是在單個語句中將變量或?qū)傩詷俗⒑涂蛇x的賦值語句合為一體:

annotated_assignment_stmt ::=  augtarget ":" expression
                               ["=" (starred_expression | yield_expression)]

與普通 賦值語句 的差別在于僅允許單個目標。

對于將簡單名稱作為賦值目標的情況,如果是在類或模塊作用域中,標注會被求值并存入一個特殊的類或模塊屬性 __annotations__ 中,這是一個將變量名稱(如為私有會被移除)映射到被求值標注的字典。 此屬性為可寫并且在類或模塊體開始執(zhí)行時如果靜態(tài)地發(fā)現(xiàn)標注就會自動創(chuàng)建。

對于將表達式作為賦值目標的情況,如果是在類或模塊作用域中,標注會被求值,但不會保存。

如果一個名稱在函數(shù)作用域內(nèi)被標注,則該名稱為該作用域的局部變量。 標注絕不會在函數(shù)作用域內(nèi)被求值和保存。

如果存在右手邊,帶標注的賦值會在對標注求值之前(如果適用)執(zhí)行實際的賦值。 如果用作表達式目標的右手邊不存在,則解釋器會對目標求值,但最后的 __setitem__()__setattr__() 調(diào)用除外。

參見

PEP 526 - 變量標注的語法

該提議增加了標注變量(也包括類變量和實例變量)類型的語法,而不再是通過注釋來進行表達。

PEP 484 - 類型提示

該提議增加了 typing 模塊以便為類型標注提供標準句法,可被靜態(tài)分析工具和 IDE 所使用。

在 3.8 版更改: 現(xiàn)在帶有標注的賦值允許在右邊以同樣的表達式作為常規(guī)賦值。 之前,某些表達式(例如未加圓括號的元組表達式)會導致語法錯誤。

7.3. assert 語句?

assert 語句是在程序中插入調(diào)試性斷言的簡便方式:

assert_stmt ::=  "assert" expression ["," expression]

簡單形式 assert expression 等價于

if __debug__:
    if not expression: raise AssertionError

擴展形式 assert expression1, expression2 等價于

if __debug__:
    if not expression1: raise AssertionError(expression2)

以上等價形式假定 __debug__AssertionError 指向具有指定名稱的內(nèi)置變量。 在當前實現(xiàn)中,內(nèi)置變量 __debug__ 在正常情況下為 True,在請求優(yōu)化時為 False (對應命令行選項為 -O)。 如果在編譯時請求優(yōu)化,當前代碼生成器不會為 assert 語句發(fā)出任何代碼。 請注意不必在錯誤信息中包含失敗表達式的源代碼;它會被作為棧追蹤的一部分被顯示。

賦值給 __debug__ 是非法的。 該內(nèi)置變量的值會在解釋器啟動時確定。

7.4. pass 語句?

pass_stmt ::=  "pass"

pass 是一個空操作 --- 當它被執(zhí)行時,什么都不發(fā)生。 它適合當語法上需要一條語句但并不需要執(zhí)行任何代碼時用來臨時占位,例如:

def f(arg): pass    # a function that does nothing (yet)

class C: pass       # a class with no methods (yet)

7.5. del 語句?

del_stmt ::=  "del" target_list

刪除是遞歸定義的,與賦值的定義方式非常類似。 此處不再詳細說明,只給出一些提示。

目標列表的刪除將從左至右遞歸地刪除每一個目標。

名稱的刪除將從局部或全局命名空間中移除該名稱的綁定,具體作用域的確定是看該名稱是否有在同一代碼塊的 global 語句中出現(xiàn)。 如果該名稱未被綁定,將會引發(fā) NameError。

屬性引用、抽取和切片的刪除會被傳遞給相應的原型對象;刪除一個切片基本等價于賦值為一個右側(cè)類型的空切片(但即便這一點也是由切片對象決定的)。

在 3.2 版更改: 在之前版本中,如果一個名稱作為被嵌套代碼塊中的自由變量出現(xiàn),則將其從局部命名空間中刪除是非法的。

7.6. return 語句?

return_stmt ::=  "return" [expression_list]

return 在語法上只會出現(xiàn)于函數(shù)定義所嵌套的代碼,不會出現(xiàn)于類定義所嵌套的代碼。

如果提供了表達式列表,它將被求值,否則以 None 替代。

return 會離開當前函數(shù)調(diào)用,并以表達式列表 (或 None) 作為返回值。

return 將控制流傳出一個帶有 finally 子句的 try 語句時,該 finally 子句會先被執(zhí)行然后再真正離開該函數(shù)。

在一個生成器函數(shù)中,return 語句表示生成器已完成并將導致 StopIteration 被引發(fā)。 返回值(如果有的話)會被當作一個參數(shù)用來構(gòu)建 StopIteration 并成為 StopIteration.value 屬性。

在一個異步生成器函數(shù)中,一個空的 return 語句表示異步生成器已完成并將導致 StopAsyncIteration 被引發(fā)。 一個非空的 return 語句在異步生成器函數(shù)中會導致語法錯誤。

7.7. yield 語句?

yield_stmt ::=  yield_expression

yield 語句在語義上等同于 yield 表達式。 yield 語句可用來省略在使用等效的 yield 表達式語句時所必須的圓括號。 例如,以下 yield 語句

yield <expr>
yield from <expr>

等同于以下 yield 表達式語句

(yield <expr>)
(yield from <expr>)

yield 表達式和語句僅在定義 generator 函數(shù)時使用,并且僅被用于生成器函數(shù)的函數(shù)體內(nèi)部。 在函數(shù)定義中使用 yield 就足以使得該定義創(chuàng)建的是生成器函數(shù)而非普通函數(shù)。

有關 yield 語義的完整細節(jié)請參看 yield 表達式 一節(jié)。

7.8. raise 語句?

raise_stmt ::=  "raise" [expression ["from" expression]]

If no expressions are present, raise re-raises the exception that is currently being handled, which is also known as the active exception. If there isn't currently an active exception, a RuntimeError exception is raised indicating that this is an error.

否則的話,raise 會將第一個表達式求值為異常對象。 它必須為 BaseException 的子類或?qū)嵗?如果它是一個類,當需要時會通過不帶參數(shù)地實例化該類來獲得異常的實例。

異常的 類型 為異常實例的類, 為實例本身。

A traceback object is normally created automatically when an exception is raised and attached to it as the __traceback__ attribute, which is writable. You can create an exception and set your own traceback in one step using the with_traceback() exception method (which returns the same exception instance, with its traceback set to its argument), like so:

raise Exception("foo occurred").with_traceback(tracebackobj)

from 子句用于異常串連:如果有該子句,則第二個 表達式 必須為另一個異常類或?qū)嵗?如果第二個表達式是一個異常實例,它將作為可寫的 __cause__ 屬性被關聯(lián)到所引發(fā)的異常。 如果該表達式是一個異常類,這個類將被實例化且所生成的異常實例將作為 __cause__ 屬性被關聯(lián)到所引發(fā)的異常。 如果所引發(fā)的異常未被處理,則兩個異常都將被打印出來:

>>>
>>> try:
...     print(1 / 0)
... except Exception as exc:
...     raise RuntimeError("Something bad happened") from exc
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

A similar mechanism works implicitly if a new exception is raised when an exception is already being handled. An exception may be handled when an except or finally clause, or a with statement, is used. The previous exception is then attached as the new exception's __context__ attribute:

>>>
>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened")
...
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

異常串連可通過在 from 子句中指定 None 來顯式地加以抑制:

>>>
>>> try:
...     print(1 / 0)
... except:
...     raise RuntimeError("Something bad happened") from None
...
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
RuntimeError: Something bad happened

有關異常的更多信息可在 異常 一節(jié)查看,有關處理異常的信息可在 try 語句 一節(jié)查看。

在 3.3 版更改: None 現(xiàn)在允許被用作 raise X from Y 中的 Y

3.3 新版功能: 使用 __suppress_context__ 屬性來抑制異常上下文的自動顯示。

在 3.11 版更改: If the traceback of the active exception is modified in an except clause, a subsequent raise statement re-raises the exception with the modified traceback. Previously, the exception was re-raised with the traceback it had when it was caught.

7.9. break 語句?

break_stmt ::=  "break"

break 在語法上只會出現(xiàn)于 forwhile 循環(huán)所嵌套的代碼,但不會出現(xiàn)于該循環(huán)內(nèi)部的函數(shù)或類定義所嵌套的代碼。

它會終結(jié)最近的外層循環(huán),如果循環(huán)有可選的 else 子句,也會跳過該子句。

如果一個 for 循環(huán)被 break 所終結(jié),該循環(huán)的控制目標會保持其當前值。

break 將控制流傳出一個帶有 finally 子句的 try 語句時,該 finally 子句會先被執(zhí)行然后再真正離開該循環(huán)。

7.10. continue 語句?

continue_stmt ::=  "continue"

continue 在語法上只會出現(xiàn)于 forwhile 循環(huán)所嵌套的代碼中,但不會出現(xiàn)于該循環(huán)內(nèi)部的函數(shù)或類定義中。 它會繼續(xù)執(zhí)行最近的外層循環(huán)的下一個輪次。

continue 將控制流傳出一個帶有 finally 子句的 try 語句時,該 finally 子句會先被執(zhí)行然后再真正開始循環(huán)的下一個輪次。

7.11. import 語句?

import_stmt     ::=  "import" module ["as" identifier] ("," module ["as" identifier])*
                     | "from" relative_module "import" identifier ["as" identifier]
                     ("," identifier ["as" identifier])*
                     | "from" relative_module "import" "(" identifier ["as" identifier]
                     ("," identifier ["as" identifier])* [","] ")"
                     | "from" relative_module "import" "*"
module          ::=  (identifier ".")* identifier
relative_module ::=  "."* module | "."+

基本的 import 語句(不帶 from 子句)會分兩步執(zhí)行:

  1. 查找一個模塊,如果有必要還會加載并初始化模塊。

  2. 在局部命名空間中為 import 語句發(fā)生位置所處的作用域定義一個或多個名稱。

當語句包含多個子句(由逗號分隔)時這兩個步驟將對每個子句分別執(zhí)行,如同這些子句被分成獨立的 import 語句一樣。

第一個步驟即查找和加載模塊的詳情 導入系統(tǒng) 一節(jié)中有更詳細的描述,其中也描述了可被導入的多種類型的包和模塊,以及可用于定制導入系統(tǒng)的所有鉤子對象。 請注意這一步如果失敗,則可能說明模塊無法找到,或者 是在初始化模塊,包括執(zhí)行模塊代碼期間發(fā)生了錯誤。

如果成功獲取到請求的模塊,則可以通過以下三種方式一之在局部命名空間中使用它:

  • 模塊名后使用 as 時,直接把 as 后的名稱與導入模塊綁定。

  • 如果沒有指定其他名稱,且被導入的模塊為最高層級模塊,則模塊的名稱將被綁定到局部命名空間作為對所導入模塊的引用。

  • 如果被導入的模塊 不是 最高層級模塊,則包含該模塊的最高層級包的名稱將被綁定到局部命名空間作為對該最高層級包的引用。 所導入的模塊必須使用其完整限定名稱來訪問而不能直接訪問。

from 形式使用的過程略微繁復一些:

  1. 查找 from 子句中指定的模塊,如有必要還會加載并初始化模塊;

  2. 對于 import 子句中指定的每個標識符:

    1. 檢查被導入模塊是否有該名稱的屬性

    2. 如果沒有,嘗試導入具有該名稱的子模塊,然后再次檢查被導入模塊是否有該屬性

    3. 如果未找到該屬性,則引發(fā) ImportError。

    4. 否則的話,將對該值的引用存入局部命名空間,如果有 as 子句則使用其指定的名稱,否則使用該屬性的名稱

示例:

import foo                 # foo imported and bound locally
import foo.bar.baz         # foo, foo.bar, and foo.bar.baz imported, foo bound locally
import foo.bar.baz as fbb  # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as fbb
from foo.bar import baz    # foo, foo.bar, and foo.bar.baz imported, foo.bar.baz bound as baz
from foo import attr       # foo imported and foo.attr bound as attr

如果標識符列表改為一個星號 ('*'),則在模塊中定義的全部公有名稱都將按 import 語句所在的作用域被綁定到局部命名空間。

一個模塊所定義的 公有名稱 是由在模塊的命名空間中檢測一個名為 __all__ 的變量來確定的;如果有定義,它必須是一個字符串列表,其中的項為該模塊所定義或?qū)氲拿Q。 在 __all__ 中所給出的名稱都會被視為公有并且應當存在。 如果 __all__ 沒有被定義,則公有名稱的集合將包含在模塊的命名空間中找到的所有不以下劃線字符 ('_') 打頭的名稱。 __all__ 應當包括整個公有 API。 它的目標是避免意外地導出不屬于 API 的一部分的項(例如在模塊內(nèi)部被導入和使用的庫模塊)。

通配符形式的導入 --- from module import * --- 僅在模塊層級上被允許。 嘗試在類或函數(shù)定義中使用它將引發(fā) SyntaxError

當指定要導入哪個模塊時,你不必指定模塊的絕對名稱。 當一個模塊或包被包含在另一個包之中時,可以在同一個最高層級包中進行相對導入,而不必提及包名稱。 通過在 from 之后指定的模塊或包中使用前綴點號,你可以在不指定確切名稱的情況下指明在當前包層級結(jié)構(gòu)中要上溯多少級。 一個前綴點號表示是執(zhí)行導入的模塊所在的當前包,兩個點號表示上溯一個包層級。 三個點號表示上溯兩級,依此類推。 因此如果你執(zhí)行 from . import mod 時所處位置為 pkg 包內(nèi)的一個模塊,則最終你將導入 pkg.mod。 如果你執(zhí)行 from ..subpkg2 import mod 時所處位置為 pkg.subpkg1 則你將導入 pkg.subpkg2.mod。 有關相對導入的規(guī)范說明包含在 包相對導入 一節(jié)中。

importlib.import_module() 被提供用來為動態(tài)地確定要導入模塊的應用提供支持。

引發(fā)一個 審計事件 import 附帶參數(shù) module, filename, sys.path, sys.meta_path, sys.path_hooks。

7.11.1. future 語句?

future 語句 是一種針對編譯器的指令,指明某個特定模塊應當使用在特定的未來某個 Python 發(fā)行版中成為標準特性的語法或語義。

future 語句的目的是使得向在語言中引入了不兼容改變的 Python 未來版本的遷移更為容易。 它允許基于每個模塊在某種新特性成為標準之前的發(fā)行版中使用該特性。

future_stmt ::=  "from" "__future__" "import" feature ["as" identifier]
                 ("," feature ["as" identifier])*
                 | "from" "__future__" "import" "(" feature ["as" identifier]
                 ("," feature ["as" identifier])* [","] ")"
feature     ::=  identifier

future 語句必須在靠近模塊開頭的位置出現(xiàn)。 可以出現(xiàn)在 future 語句之前行只有:

  • 模塊的文檔字符串(如果存在),

  • 注釋,

  • 空行,以及

  • 其他 future 語句。

唯一需要使用 future 語句的特性是 標注 (參見 PEP 563)。

future 語句所啟用的所有歷史特性仍然為 Python 3 所認可。 其中包括 absolute_import, division, generators, generator_stop, unicode_literals, print_function, nested_scopeswith_statement。 它們都已成為冗余項,因為它們總是為已啟用狀態(tài),保留它們只是為了向后兼容。

future 語句在編譯時會被識別并做特殊對待:對核心構(gòu)造語義的改變常常是通過生成不同的代碼來實現(xiàn)。 新的特性甚至可能會引入新的不兼容語法(例如新的保留字),在這種情況下編譯器可能需要以不同的方式來解析模塊。 這樣的決定不能推遲到運行時方才作出。

對于任何給定的發(fā)布版本,編譯器要知道哪些特性名稱已被定義,如果某個 future 語句包含未知的特性則會引發(fā)編譯時錯誤。

直接運行時的語義與任何 import 語句相同:存在一個后文將詳細說明的標準模塊 __future__,它會在執(zhí)行 future 語句時以通常的方式被導入。

相應的運行時語義取決于 future 語句所啟用的指定特性。

請注意以下語句沒有任何特別之處:

import __future__ [as name]

這并非 future 語句;它只是一條沒有特殊語義或語法限制的普通 import 語句。

在默認情況下,通過對Code compiled by calls to the 內(nèi)置函數(shù) exec()compile() 的調(diào)用所編譯的代碼如果出現(xiàn)于一個包含有 future 語句的模塊 M 之中,就會使用 future 語句所關聯(lián)的語法和語義。 此行為可以通過 compile() 的可選參數(shù)加以控制 --- 請參閱該函數(shù)的文檔以了解詳情。

在交互式解釋器提示符中鍵入的 future 語句將在解釋器會話此后的交互中有效。 如果一個解釋器的啟動使用了 -i 選項啟動,并傳入了一個腳本名稱來執(zhí)行,且該腳本包含 future 語句,它將在交互式會話開始執(zhí)行腳本之后保持有效。

參見

PEP 236 - 回到 __future__

有關 __future__ 機制的最初提議。

7.12. global 語句?

global_stmt ::=  "global" identifier ("," identifier)*

global 語句是作用于整個當前代碼塊的聲明。 它意味著所列出的標識符將被解讀為全局變量。 要給全局變量賦值不可能不用到 global 關鍵字,不過自由變量也可以指向全局變量而不必聲明為全局變量。

global 語句中列出的名稱不得在同一代碼塊內(nèi)該 global 語句之前的位置中使用。

global 語句中列出的名稱不能被定義為形式參數(shù),也不能被作為 with 語句或 except 子句的目標,以及 for 循環(huán)的目標列表、class 定義、函數(shù)定義、import 語句或變量標注等等。

CPython implementation detail: 當前的實現(xiàn)并未強制要求所有的上述限制,但程序不應當濫用這樣的自由,因為未來的實現(xiàn)可能會改為強制要求,并靜默地改變程序的含義。

程序員注意事項: global 是對解析器的指令。 它僅對與 global 語句同時被解析的代碼起作用。 特別地,包含在提供給內(nèi)置 exec() 函數(shù)字符串或代碼對象中的 global 語句并不會影響 包含 該函數(shù)調(diào)用的代碼塊,而包含在這種字符串中的代碼也不會受到包含該函數(shù)調(diào)用的代碼中的 global 語句影響。 這同樣適用于 eval()compile() 函數(shù)。

7.13. nonlocal 語句?

nonlocal_stmt ::=  "nonlocal" identifier ("," identifier)*

nonlocal 語句會使得所列出的名稱指向之前在最近的包含作用域中綁定的除全局變量以外的變量。 這種功能很重要,因為綁定的默認行為是先搜索局部命名空間。 這個語句允許被封裝的代碼重新綁定局部作用域以外且非全局(模塊)作用域當中的變量。

global 語句中列出的名稱不同,nonlocal 語句中列出的名稱必須指向之前存在于包含作用域之中的綁定(在這個應當用來創(chuàng)建新綁定的作用域不能被無歧義地確定)。

nonlocal 語句中列出的名稱不得與之前存在于局部作用域中的綁定相沖突。

參見

PEP 3104 - 訪問外層作用域中的名稱

有關 nonlocal 語句的規(guī)范說明。