3. 資料模型¶
3.1. 物件、值和型別¶
物件是 Python 對資料的抽象。Python 程式中的所有資料都由物件或物件之間的關係表示。(在某種意義上,並且符合馮·諾伊曼的“儲存程式計算機”模型,程式碼也由物件表示。)
每個物件都有一個標識、一個型別和一個值。物件的標識一旦建立就永遠不會改變;你可以把它想象成物件在記憶體中的地址。is
運算子比較兩個物件的標識;id()
函式返回一個表示其標識的整數。
CPython 實現細節: 對於 CPython,id(x)
是 x
儲存的記憶體地址。
物件的型別決定了該物件支援的操作(例如,“它是否有長度?”),並且還定義了該型別物件的可能值。type()
函式返回物件的型別(它本身也是一個物件)。與它的標識一樣,物件的 型別 也是不可變的。[1]
某些物件的值可以更改。值可以更改的物件被稱為可變的;一旦建立其值就不可更改的物件被稱為不可變的。(當可變物件的值更改時,包含對可變物件的引用的不可變容器物件的值可能會更改;但是,該容器仍然被認為是不可變的,因為它包含的物件集合無法更改。因此,不可變性並不嚴格等同於擁有不可更改的值,它更加微妙。)物件的變異性由其型別決定;例如,數字、字串和元組是不可變的,而字典和列表是可變的。
物件永遠不會被顯式銷燬;但是,當它們變得不可訪問時,它們可能會被垃圾回收。允許實現延遲垃圾回收或完全省略垃圾回收——垃圾回收的實現方式是實現質量的問題,只要沒有收集到仍然可訪問的物件即可。
CPython 實現細節: CPython 目前使用引用計數方案,並(可選地)延遲檢測迴圈連結的垃圾。這種方案會在大多數物件變為不可達時立即回收它們,但不能保證回收包含迴圈引用的垃圾。有關控制迴圈垃圾回收的資訊,請參閱 gc
模組的文件。其他實現的行為可能不同,CPython 也可能會發生變化。不要依賴於物件變為不可達時立即進行終結(因此,您應該始終顯式關閉檔案)。
請注意,使用實現的跟蹤或除錯功能可能會使通常可以回收的物件保持活動狀態。另請注意,使用 try
…except
語句捕獲異常可能會使物件保持活動狀態。
某些物件包含對“外部”資源的引用,例如開啟的檔案或視窗。可以理解的是,當物件被垃圾回收時,這些資源會被釋放,但由於不能保證會發生垃圾回收,因此這些物件還提供了一種顯式釋放外部資源的方法,通常是 close()
方法。強烈建議程式顯式關閉此類物件。try
…finally
語句和 with
語句提供了執行此操作的便捷方法。
某些物件包含對其他物件的引用;這些物件被稱為容器。容器的示例包括元組、列表和字典。這些引用是容器值的一部分。在大多數情況下,當我們談論容器的值時,我們指的是包含物件的值,而不是它們的標識;但是,當我們談論容器的可變性時,僅指立即包含的物件的標識。因此,如果不可變容器(如元組)包含對可變物件的引用,則如果該可變物件被更改,其值也會更改。
型別會影響物件行為的幾乎所有方面。甚至物件標識的重要性在某種意義上也會受到影響:對於不可變型別,計算新值的操作實際上可能會返回對具有相同型別和值的任何現有物件的引用,而對於可變物件,則不允許這樣做。例如,在 a = 1; b = 1
之後,a 和 b 可能指向也可能不指向同一個值為 1 的物件,具體取決於實現。這是因為 int
是一個不可變型別,因此可以重用對 1
的引用。此行為取決於所使用的實現,因此不應依賴它,但在使用物件標識測試時應注意這一點。但是,在 c = []; d = []
之後,保證 c 和 d 指向兩個不同且唯一的新建立的空列表。(請注意,e = f = []
將相同的物件分配給 e 和 f。)
3.2. 標準型別層次結構¶
以下是 Python 中內建的型別列表。擴充套件模組(用 C、Java 或其他語言編寫,具體取決於實現)可以定義其他型別。未來版本的 Python 可能會向型別層次結構新增型別(例如,有理數、有效儲存的整數陣列等),儘管此類新增通常會透過標準庫提供。
以下某些型別描述包含一個段落,列出“特殊屬性”。這些屬性提供對實現的訪問,不適合一般使用。它們的定義將來可能會更改。
3.2.1. None¶
此型別只有一個值。此值只有一個物件。可以透過內建名稱 None
訪問此物件。它用於表示許多情況下缺少值,例如,它從不顯式返回任何內容的函式返回。其真值為假。
3.2.2. NotImplemented¶
此型別只有一個值。此值只有一個物件。可以透過內建名稱 NotImplemented
訪問此物件。如果數值方法和豐富的比較方法沒有為提供的運算元實現該操作,則應返回此值。(直譯器將嘗試反射操作或其他回退,具體取決於運算子。)不應在布林上下文中對其求值。
有關更多詳細資訊,請參閱 實現算術運算。
在 3.9 版本中更改:在布林上下文中評估 NotImplemented
已被棄用。雖然它目前評估為真,但它會發出 DeprecationWarning
。在未來版本的 Python 中,它將引發 TypeError
。
3.2.3. Ellipsis¶
此型別只有一個值。此值只有一個物件。可以透過字面量 ...
或內建名稱 Ellipsis
訪問此物件。其真值為真。
3.2.4. numbers.Number
¶
這些是透過數字字面量建立的,並作為算術運算子和算術內建函式的結果返回。數值物件是不可變的;一旦建立,它們的值永遠不會改變。Python 數字當然與數學數字密切相關,但受計算機中數值表示的限制。
數值類的字串表示形式(由 __repr__()
和 __str__()
計算)具有以下屬性
它們是有效的數值字面量,當傳遞給其類建構函式時,會生成一個具有原始數值值的物件。
可能時,表示形式為 10 進位制。
不顯示前導零,可能除了小數點前的單個零。
不顯示尾隨零,可能除了小數點後的單個零。
僅當數字為負數時才顯示符號。
Python 區分整數、浮點數和複數
3.2.4.1. numbers.Integral
¶
這些表示來自數學整數集(正數和負數)的元素。
注意
整數表示的規則旨在給出涉及負整數的移位和掩碼操作的最有意義的解釋。
有兩種型別的整數
3.2.4.2. numbers.Real
(float
)¶
這些表示機器級雙精度浮點數。您必須受制於底層機器架構(以及 C 或 Java 實現)對於可接受的範圍和溢位處理。Python 不支援單精度浮點數;通常使用這些數字的原因是為了節省處理器和記憶體使用,但它們與在 Python 中使用物件的開銷相比相形見絀,因此沒有理由用兩種浮點數來複雜化語言。
3.2.4.3. numbers.Complex
(complex
)¶
這些表示複數,使用一對機器級的雙精度浮點數。浮點數的所有注意事項同樣適用於複數。複數 z
的實部和虛部可以透過只讀屬性 z.real
和 z.imag
獲取。
3.2.5. 序列¶
這些表示由非負數索引的有限有序集合。內建函式 len()
返回序列中專案的數量。當序列的長度為 *n* 時,索引集包含數字 0, 1, …, *n*-1。序列 *a* 的第 *i* 項透過 a[i]
選取。一些序列,包括內建序列,透過加上序列長度來解釋負下標。例如,a[-2]
等於 a[n-2]
,即長度為 n
的序列 a 的倒數第二項。
序列也支援切片:a[i:j]
選擇所有索引 *k* 滿足 i <=
k <
j 的項。當用作表示式時,切片是相同型別的序列。上面關於負索引的註釋也適用於負切片位置。
一些序列還支援帶有第三個“步長”引數的“擴充套件切片”:a[i:j:k]
選擇 *a* 中索引為 *x* 的所有項,其中 x = i + n*k
, n >=
0
且 i <=
x <
j。
序列根據其可變性進行區分
3.2.5.1. 不可變序列¶
不可變序列型別的物件一旦建立就不能更改。(如果物件包含對其他物件的引用,則這些其他物件可能是可變的並且可能會被更改;但是,不可變物件直接引用的物件集合不能更改。)
以下型別是不可變序列
- 字串
字串是表示 Unicode 程式碼點的數值序列。範圍
U+0000 - U+10FFFF
中的所有程式碼點都可以在字串中表示。Python 沒有 char 型別;相反,字串中的每個程式碼點都表示為一個長度為1
的字串物件。內建函式ord()
將程式碼點從其字串形式轉換為範圍0 - 10FFFF
中的整數;chr()
將範圍0 - 10FFFF
中的整數轉換為相應的長度為1
的字串物件。str.encode()
可以用來使用給定的文字編碼將str
轉換為bytes
,bytes.decode()
可以用來實現相反的操作。- 元組
元組的專案是任意 Python 物件。兩個或多個專案的元組由以逗號分隔的表示式列表形成。可以透過在表示式後附加逗號來形成一個專案的元組(“單例”)(表示式本身不會建立元組,因為括號必須可用於對錶達式進行分組)。可以透過空括號對形成一個空元組。
- 位元組
位元組物件是不可變陣列。專案是 8 位位元組,由範圍 0 <= x < 256 中的整數表示。位元組字面量(如
b'abc'
)和內建的bytes()
建構函式可用於建立位元組物件。此外,位元組物件可以透過decode()
方法解碼為字串。
3.2.5.2. 可變序列¶
可變序列可以在建立後進行更改。下標和切片表示法可以用作賦值和 del
(刪除) 語句的目標。
注意
collections
和 array
模組提供了可變序列型別的其他示例。
目前有兩種內在的可變序列型別
- 列表
列表的專案是任意 Python 物件。列表是透過將以逗號分隔的表示式列表放在方括號中形成的。(請注意,不需要特殊情況來形成長度為 0 或 1 的列表。)
- 位元組陣列
位元組陣列物件是可變陣列。它們由內建的
bytearray()
建構函式建立。除了是可變的(因此不可雜湊)之外,位元組陣列在其他方面提供了與不可變的bytes
物件相同的介面和功能。
3.2.6. 集合型別¶
這些表示無序的、有限的、唯一的不可變物件集合。因此,它們不能透過任何下標索引。但是,它們可以被迭代,並且內建函式 len()
返回集合中專案的數量。集合的常見用途是快速成員資格測試、從序列中刪除重複項以及計算數學運算,例如交集、並集、差集和對稱差集。
對於集合元素,與字典鍵相同的不可變性規則適用。請注意,數字型別遵循數字比較的正常規則:如果兩個數字比較相等(例如,1
和 1.0
),則它們中只能有一個包含在集合中。
目前有兩種內在的集合型別
- 集合
這些表示可變集合。它們由內建的
set()
建構函式建立,並且可以通過幾種方法(例如add()
)進行修改。- 凍結集合
這些表示不可變集合。它們由內建的
frozenset()
建構函式建立。由於 frozenset 是不可變的並且 可雜湊,因此可以再次用作另一個集合的元素或字典鍵。
3.2.7. 對映¶
這些表示由任意索引集索引的物件的有限集合。下標表示法 a[k]
從對映 a
中選擇由 k
索引的專案;這可以用在表示式中,也可以用作賦值或 del
語句的目標。內建函式 len()
返回對映中專案的數量。
目前只有一種內在的對映型別
3.2.7.1. 字典¶
這些表示由幾乎任意的值索引的物件的有限集合。唯一不能接受作為鍵的值是包含列表或字典或其他透過值而不是物件標識進行比較的可變型別的值,原因是字典的有效實現要求鍵的雜湊值保持不變。用於鍵的數字型別遵循數字比較的正常規則:如果兩個數字比較相等(例如,1
和 1.0
),則它們可以互換使用來索引相同的字典條目。
字典保留插入順序,這意味著鍵將以它們在字典中按順序新增的相同順序生成。替換現有鍵不會更改順序,但是刪除一個鍵並重新插入它會將其新增到末尾,而不是保留其舊位置。
字典是可變的;它們可以透過 {}
表示法建立(請參閱字典顯示部分)。
擴充套件模組 dbm.ndbm
和 dbm.gnu
提供了對映型別的其他示例,collections
模組也提供了。
3.7 版本已更改: 在 Python 3.6 之前的版本中,字典不保留插入順序。在 CPython 3.6 中,保留了插入順序,但當時被認為是實現細節,而不是語言保證。
3.2.8. 可呼叫型別¶
這些型別是可以應用函式呼叫操作的型別(參見 呼叫 部分)
3.2.8.1. 使用者自定義函式¶
使用者自定義函式物件是透過函式定義建立的(參見 函式定義 部分)。在呼叫時,它應該使用與函式的形參列表項數相同的實參列表。
3.2.8.1.1. 特殊的只讀屬性¶
屬性 |
含義 |
---|---|
|
|
|
單元格物件具有屬性 |
3.2.8.1.2. 特殊的 可寫屬性¶
大多數這些屬性都會檢查所賦值的型別
屬性 |
含義 |
---|---|
|
該函式的文件字串,如果不可用,則為 |
|
該函式的名稱。另請參見: |
|
該函式的限定名稱。另請參見: 3.3 版本新增。 |
|
定義該函式的模組的名稱,如果不可用,則為 |
|
|
|
表示已編譯函式體的程式碼物件。 |
|
支援任意函式屬性的名稱空間。另請參見: |
|
一個 |
|
|
|
3.12 版本新增。 |
函式物件還支援獲取和設定任意屬性,例如,這可以用於將元資料附加到函式。 使用常規屬性點號表示法來獲取和設定此類屬性。
CPython 實現細節: CPython 的當前實現僅支援使用者自定義函式上的函式屬性。未來可能會支援內建函式上的函式屬性。
3.2.8.2. 例項方法¶
例項方法物件將類、類例項和任何可呼叫物件(通常是使用者自定義函式)組合在一起。
特殊的只讀屬性
|
指向該方法繫結到的類例項物件 |
|
指向原始函式物件 |
|
該方法的文件(與 |
|
該方法的名稱(與 |
|
定義該方法的模組的名稱,如果不可用,則為 |
方法還支援訪問(但不設定)底層函式物件上的任意函式屬性。
當獲取類的屬性(可能透過該類的例項)時,如果該屬性是使用者定義的函式物件或classmethod
物件,則可以建立使用者定義的方法物件。
當透過類的例項檢索類中的使用者定義的函式物件時建立例項方法物件,其__self__
屬性是該例項,並且該方法物件被稱為是*繫結的*。新方法的__func__
屬性是原始函式物件。
當從類或例項中檢索 classmethod
物件時建立例項方法物件,其 __self__
屬性是該類本身,其 __func__
屬性是類方法下的函式物件。
當呼叫例項方法物件時,會呼叫底層函式(__func__
),並將類例項(__self__
)插入到引數列表的前面。例如,當 C
是一個包含函式 f()
定義的類,而 x
是 C
的一個例項時,呼叫 x.f(1)
等價於呼叫 C.f(x, 1)
。
當例項方法物件派生自 classmethod
物件時,儲存在 __self__
中的“類例項”實際上將是類本身,因此呼叫 x.f(1)
或 C.f(1)
都等價於呼叫 f(C,1)
,其中 f
是底層函式。
需要注意的是,作為類例項屬性的使用者定義函式不會轉換為繫結方法;只有當函式是類的屬性時才會發生這種情況。
3.2.8.3. 生成器函式¶
使用 yield
語句(參見 yield 語句 部分)的函式或方法稱為生成器函式。這種函式在呼叫時,總是返回一個 迭代器 物件,該物件可用於執行函式體:呼叫迭代器的 iterator.__next__()
方法將導致函式執行,直到它使用 yield
語句提供一個值。當函式執行 return
語句或執行到末尾時,會引發 StopIteration
異常,並且迭代器將到達要返回的值的集合的末尾。
3.2.8.4. 協程函式¶
使用 async def
定義的函式或方法稱為協程函式。這種函式在呼叫時,會返回一個 協程 物件。它可以包含 await
表示式,以及 async with
和 async for
語句。另請參閱 協程物件 部分。
3.2.8.5. 非同步生成器函式¶
使用 async def
定義並且使用 yield
語句的函式或方法稱為非同步生成器函式。這種函式在呼叫時,返回一個 非同步迭代器 物件,該物件可以在 async for
語句中使用,以執行函式體。
呼叫非同步迭代器的 aiterator.__anext__
方法將返回一個 可等待物件,當等待該物件時,它將執行直到使用 yield
表示式提供一個值。當函式執行空的 return
語句或執行到末尾時,會引發 StopAsyncIteration
異常,並且非同步迭代器將到達要生成的值的集合的末尾。
3.2.8.6. 內建函式¶
內建函式物件是 C 函式的包裝器。內建函式的示例有 len()
和 math.sin()
(math
是一個標準的內建模組)。引數的數量和型別由 C 函式確定。特殊的只讀屬性
__doc__
是函式的文件字串,如果不可用,則為None
。請參閱function.__doc__
。__name__
是函式的名稱。請參閱function.__name__
。__self__
設定為None
(但請參閱下一項)。__module__
是函式定義所在的模組的名稱,如果不可用,則為None
。請參閱function.__module__
。
3.2.8.7. 內建方法¶
這實際上是內建函式的另一種偽裝,這次包含一個傳遞給 C 函式的物件作為隱式額外引數。內建方法的示例是 alist.append()
,假設 *alist* 是一個列表物件。在這種情況下,特殊的只讀屬性 __self__
將設定為由 *alist* 表示的物件。(該屬性的語義與 其他 例項 方法
相同。)
3.2.8.8. 類¶
類是可呼叫的。這些物件通常充當自身新例項的工廠,但對於覆蓋 __new__()
的類型別,可能存在變體。呼叫的引數傳遞給 __new__()
,並且在典型情況下,傳遞給 __init__()
來初始化新例項。
3.2.8.9. 類例項¶
透過在其類中定義 __call__()
方法,可以使任意類的例項可呼叫。
3.2.9. 模組¶
模組是 Python 程式碼的基本組織單元,由 匯入系統 建立,該系統由 import
語句呼叫,或透過呼叫諸如 importlib.import_module()
和內建 __import__()
等函式來呼叫。模組物件具有一個由 字典
物件實現的名稱空間(這是模組中定義的函式的 __globals__
屬性引用的字典)。屬性引用將轉換為在此字典中的查詢,例如,m.x
等價於 m.__dict__["x"]
。模組物件不包含用於初始化模組的程式碼物件(因為一旦初始化完成,就不再需要它)。
屬性賦值會更新模組的名稱空間字典,例如,m.x = 1
等價於 m.__dict__["x"] = 1
。
3.2.9.2. 模組物件上的其他可寫屬性¶
除了上面列出的與匯入相關的屬性外,模組物件還具有以下可寫屬性
- module.__doc__¶
模組的文件字串,如果不可用,則為
None
。另請參閱:__doc__ 屬性
。
- module.__annotations__¶
一個字典,其中包含在模組主體執行期間收集的 變數註解。有關使用
__annotations__
的最佳實踐,請參閱 註解最佳實踐。
3.2.9.3. 模組字典¶
模組物件還具有以下特殊只讀屬性
- module.__dict__¶
模組的名稱空間,作為字典物件。在本文列出的屬性中,
__dict__
是唯一不能從模組內部作為全域性變數訪問的屬性;它只能作為模組物件上的屬性訪問。CPython 實現細節: 由於 CPython 清理模組字典的方式,即使字典仍有活動引用,當模組超出作用域時,模組字典也會被清理。為了避免這種情況,請複製字典或在使用其字典時保留模組。
3.2.10. 自定義類¶
自定義類型別通常透過類定義建立(請參閱 類定義 部分)。類具有由字典物件實現的名稱空間。類屬性引用被轉換為在此字典中的查詢,例如,C.x
被轉換為 C.__dict__["x"]
(儘管有許多鉤子允許其他方式查詢屬性)。當在那裡找不到屬性名稱時,屬性搜尋會繼續在基類中進行。這種對基類的搜尋使用 C3 方法解析順序,即使在存在“菱形”繼承結構(其中有多條繼承路徑通向共同祖先)的情況下,該順序也能正確執行。有關 Python 使用的 C3 MRO 的更多詳細資訊,請參閱 Python 2.3 方法解析順序。
當類屬性引用(例如,對於類 C
)會產生類方法物件時,它會被轉換為例項方法物件,該例項方法物件的 __self__
屬性為 C
。當它會產生 staticmethod
物件時,它會被轉換為靜態方法物件包裝的物件。有關從類檢索的屬性可能與實際包含在其 __dict__
中的屬性不同的另一種方式,請參閱 實現描述符 部分。
類屬性賦值會更新類的字典,而不是基類的字典。
可以呼叫類物件(如上所述)來產生類例項(如下所述)。
3.2.10.1. 特殊屬性¶
屬性 |
含義 |
---|---|
|
類的名稱。另請參閱: |
|
類的 限定名稱。另請參閱: |
|
定義該類的模組的名稱。 |
|
一個 |
|
一個 |
|
類的文件字串,如果未定義,則為 |
|
一個字典,其中包含在類主體執行期間收集的 變數註解。有關使用 注意 直接訪問類物件的 |
|
3.12 版本新增。 |
|
一個 3.13 版本新增。 |
|
類定義的第一行行號,包括裝飾器。設定 3.13 版本新增。 |
|
在方法解析期間查詢基類時考慮的類的 |
3.2.10.2. 特殊方法¶
除了上面描述的特殊屬性之外,所有 Python 類還具有以下兩個可用的方法
- type.__subclasses__()¶
每個類都保留一個指向其直接子類的弱引用列表。 此方法返回所有仍然存活的引用列表。 該列表的順序為定義順序。 示例
>>> class A: pass >>> class B(A): pass >>> A.__subclasses__() [<class 'B'>]
3.2.11. 類例項¶
透過呼叫類物件(見上文)來建立類例項。 類例項具有一個以字典形式實現的名稱空間,該字典是搜尋屬性引用的第一個位置。 如果在那裡找不到屬性,並且例項的類具有該名稱的屬性,則繼續搜尋類屬性。 如果找到的類屬性是使用者定義的函式物件,則將其轉換為例項方法物件,其 __self__
屬性是該例項。 靜態方法和類方法物件也會被轉換; 請參見上面 “類” 下的內容。 有關透過例項檢索的類屬性可能與實際儲存在類的 __dict__
中的物件不同的另一種方式,請參閱實現描述符 部分。 如果沒有找到類屬性,並且該物件的類具有 __getattr__()
方法,則會呼叫該方法來滿足查詢。
屬性賦值和刪除會更新例項的字典,而不是類的字典。 如果該類具有 __setattr__()
或 __delattr__()
方法,則會呼叫該方法,而不是直接更新例項字典。
如果類例項具有某些特殊名稱的方法,則可以假裝為數字、序列或對映。 請參閱特殊方法名稱部分。
3.2.11.1. 特殊屬性¶
- object.__class__¶
類例項所屬的類。
3.2.12. I/O 物件(也稱為檔案物件)¶
檔案物件表示一個開啟的檔案。 有多種快捷方式可用於建立檔案物件:open()
內建函式,以及 os.popen()
、os.fdopen()
和套接字物件的 makefile()
方法(也可能是擴充套件模組提供的其他函式或方法)。
物件 sys.stdin
、sys.stdout
和 sys.stderr
被初始化為與直譯器的標準輸入、輸出和錯誤流相對應的檔案物件; 它們都以文字模式開啟,因此遵循 io.TextIOBase
抽象類定義的介面。
3.2.13. 內部型別¶
直譯器內部使用的一些型別會暴露給使用者。 它們的定義可能會在直譯器的未來版本中更改,但在此為了完整性而提及它們。
3.2.13.1. 程式碼物件¶
程式碼物件表示*位元組編譯的*可執行 Python 程式碼或位元組碼。 程式碼物件和函式物件之間的區別在於,函式物件包含對函式全域性變數(定義它的模組)的顯式引用,而程式碼物件不包含上下文;此外,預設引數值儲存在函式物件中,而不是在程式碼物件中(因為它們表示在執行時計算的值)。 與函式物件不同,程式碼物件是不可變的,並且不包含(直接或間接)對可變物件的引用。
3.2.13.1.1. 特殊只讀屬性¶
|
函式名稱 |
|
函式的完全限定名稱 3.11 版本新增。 |
|
函式具有的位置引數總數(包括僅限位置的引數和帶有預設值的引數) |
|
函式具有的僅限位置引數數量(包括帶有預設值的引數) |
|
函式具有的僅關鍵字引數數量(包括帶有預設值的引數) |
|
函式使用的區域性變數的數量(包括引數) |
|
包含函式中區域性變數名稱的 |
|
|
|
一個 注意:不包括對全域性名稱和內建名稱的引用。 |
|
一個字串,表示函式中位元組碼指令的序列。 |
|
|
|
|
|
編譯程式碼的檔名。 |
|
函式的第一行行號。 |
|
一個字串,編碼從位元組碼偏移到行號的對映。有關詳細資訊,請參閱直譯器的原始碼。 3.12 版本起已棄用: 程式碼物件的此屬性已棄用,可能會在 Python 3.15 中刪除。 |
|
程式碼物件所需的堆疊大小。 |
|
一個 |
為co_flags
定義了以下標誌位:如果函式使用 *arguments
語法接受任意數量的位置引數,則設定位 0x04
;如果函式使用 **keywords
語法接受任意關鍵字引數,則設定位 0x08
;如果函式是生成器,則設定位 0x20
。有關可能存在的每個標誌的語義的詳細資訊,請參閱程式碼物件位標誌。
未來特性宣告 ( from __future__ import division
) 也在co_flags
中使用位來指示是否在啟用特定特性時編譯程式碼物件:如果函式是在啟用未來除法的情況下編譯的,則設定位 0x2000
;位 0x10
和 0x1000
在早期版本的 Python 中使用。
co_flags
中的其他位保留供內部使用。
如果程式碼物件表示一個函式,則co_consts
中的第一項是函式的文件字串,如果未定義,則為 None
。
3.2.13.1.2. 程式碼物件的方法¶
- codeobject.co_positions()¶
返回程式碼物件中每個位元組碼指令的原始碼位置的可迭代物件。
迭代器返回包含
(start_line, end_line, start_column, end_column)
的元組
。第 *i* 個元組對應於編譯為第 *i* 個程式碼單元的原始碼的位置。列資訊是給定源行上的 0 索引 utf-8 位元組偏移量。此位置資訊可能會丟失。以下是一些可能發生這種情況的非詳盡列表:
使用
-X
no_debug_ranges
執行直譯器。載入在使用
-X
no_debug_ranges
時編譯的 pyc 檔案。與人工指令對應的位置元組。
由於特定於實現的限制而無法表示的行號和列號。
發生這種情況時,某些或所有元組元素可能是
None
。3.11 版本新增。
注意
此功能需要在程式碼物件中儲存列位置,這可能會導致編譯的 Python 檔案或直譯器記憶體使用量的磁碟使用量略有增加。要避免儲存額外資訊和/或停用列印額外的回溯資訊,可以使用
-X
no_debug_ranges
命令列標誌或PYTHONNODEBUGRANGES
環境變數。
- codeobject.co_lines()¶
返回一個迭代器,該迭代器產生有關連續的位元組碼範圍的資訊。產生的每一項都是一個
(start, end, lineno)
元組
產生的項將具有以下屬性:
產生的第一個範圍的
start
為 0。(start, end)
範圍將是非遞減且連續的。也就是說,對於任意一對tuple
,第二個的start
將等於第一個的end
。不會有向後範圍:對於所有三元組,
end >= start
。
允許零寬度範圍,其中
start == end
。零寬度範圍用於在原始碼中存在,但已被 位元組碼 編譯器消除的行。在 3.10 版本中新增。
另請參閱
- PEP 626 - 用於除錯和其他工具的精確行號。
引入
co_lines()
方法的 PEP。
- codeobject.replace(**kwargs)¶
返回程式碼物件的副本,其中包含指定欄位的新值。
通用函式
copy.replace()
也支援程式碼物件。在 3.8 版本中新增。
3.2.13.2. 幀物件¶
幀物件表示執行幀。它們可能出現在 回溯物件 中,並且還會傳遞給註冊的跟蹤函式。
3.2.13.2.1. 特殊的只讀屬性¶
|
指向前一個堆疊幀(朝向呼叫者),如果這是最底層的堆疊幀,則為 |
|
在此幀中執行的 程式碼物件。訪問此屬性會引發 審計事件 |
|
幀用於查詢 區域性變數 的對映。如果幀引用 最佳化作用域,則可能返回一個直寫代理物件。 在 3.13 版本中更改: 為最佳化作用域返回代理。 |
|
幀用於查詢 全域性變數 的字典 |
|
幀用於查詢 內建(內在)名稱 的字典 |
|
3.2.13.2.2. 特殊的寫入屬性¶
|
如果不是 |
|
將此屬性設定為 |
|
將此屬性設定為 |
|
幀的當前行號 – 從跟蹤函式內部寫入此屬性會跳轉到給定的行(僅適用於最底層的幀)。偵錯程式可以透過寫入此屬性來實現跳轉命令(又名設定下一條語句)。 |
3.2.13.2.3. 幀物件方法¶
幀物件支援一種方法
- frame.clear()¶
此方法清除幀持有的所有對 區域性變數 的引用。此外,如果該幀屬於 生成器,則該生成器將被最終確定。這有助於打破涉及幀物件的引用迴圈(例如,當捕獲 異常 並存儲其 回溯 以供以後使用時)。
如果幀當前正在執行或暫停,則會引發
RuntimeError
。3.4 版本新增。
在 3.13 版本中更改: 嘗試清除暫停的幀會引發
RuntimeError
(就像執行幀一直以來的情況一樣)。
3.2.13.3. 回溯物件¶
回溯物件表示 異常 的堆疊跟蹤。當發生異常時,會隱式建立回溯物件,也可以透過呼叫 types.TracebackType
來顯式建立。
在 3.7 版本中更改: 現在可以從 Python 程式碼顯式例項化回溯物件。
對於隱式建立的回溯,當搜尋異常處理程式展開執行堆疊時,在每個展開級別,回溯物件都會插入到當前回溯的前面。當輸入異常處理程式時,堆疊跟蹤可供程式使用。(請參閱 try 語句 部分。)它可以作為 sys.exc_info()
返回的元組的第三項訪問,也可以作為捕獲的異常的 __traceback__
屬性訪問。
當程式不包含合適的處理程式時,堆疊跟蹤將(格式精美地)寫入標準錯誤流;如果直譯器是互動式的,它也會作為 sys.last_traceback
提供給使用者。
對於顯式建立的回溯,如何連結 tb_next
屬性以形成完整的堆疊跟蹤取決於回溯的建立者。
特殊的只讀屬性
|
指向當前級別的執行 幀。 訪問此屬性會引發 審計事件 |
|
給出發生異常的行號 |
|
指示“精確指令”。 |
如果異常發生在沒有匹配的 except 子句或帶有 finally
子句的 try
語句中,則回溯中的行號和最後一條指令可能與其 幀物件 的行號不同。
- traceback.tb_next¶
特殊的、可寫的屬性
tb_next
是堆疊跟蹤中的下一層(朝向發生異常的幀),如果沒有下一層,則為None
。在 3.7 版本中更改: 此屬性現在可寫
3.2.13.4. 切片物件¶
切片物件用於表示 __getitem__()
方法的切片。它們也由內建的 slice()
函式建立。
特殊的只讀屬性:start
是下界;stop
是上界;step
是步長值;如果省略,則每個都為 None
。這些屬性可以具有任何型別。
切片物件支援一個方法
- slice.indices(self, length)¶
此方法接受一個整數引數 length,並計算如果將切片物件應用於長度為 length 的項的序列時,該切片物件將描述的切片的資訊。它返回一個包含三個整數的元組;分別為切片的 start 和 stop 索引以及 step 或跨度長度。缺失或超出範圍的索引的處理方式與常規切片一致。
3.2.13.5. 靜態方法物件¶
靜態方法物件提供了一種克服上述函式物件到方法物件的轉換的方法。靜態方法物件是任何其他物件的包裝器,通常是使用者定義的方法物件。當從類或類例項檢索靜態方法物件時,實際返回的物件是被包裝的物件,該物件不受任何進一步轉換的影響。靜態方法物件也是可呼叫的。靜態方法物件由內建的 staticmethod()
建構函式建立。
3.2.13.6. 類方法物件¶
類方法物件,就像靜態方法物件一樣,是另一個物件的包裝器,它會改變從類和類例項中檢索該物件的方式。類方法物件在此類檢索時的行為在上面的 “例項方法” 中進行了描述。類方法物件由內建的 classmethod()
建構函式建立。
3.3. 特殊方法名稱¶
類可以透過定義具有特殊名稱的方法來實現由特殊語法(例如算術運算或下標和切片)呼叫的某些操作。這是 Python 的 運算子過載 方法,允許類定義它們自己關於語言運算子的行為。例如,如果一個類定義了一個名為 __getitem__()
的方法,並且 x
是此類的例項,那麼 x[i]
大致等價於 type(x).__getitem__(x, i)
。除非另有說明,否則當沒有定義適當的方法時,嘗試執行操作會引發異常(通常為 AttributeError
或 TypeError
)。
將特殊方法設定為 None
表示相應的操作不可用。例如,如果一個類將 __iter__()
設定為 None
,則該類不可迭代,因此在其例項上呼叫 iter()
將引發 TypeError
(而不會回退到 __getitem__()
)。[2]
在實現模擬任何內建型別的類時,重要的是模擬的實現程度應與所建模物件有意義的程度相符。例如,某些序列可能非常適合檢索單個元素,但提取切片可能沒有意義。(這方面的一個例子是 W3C 文件物件模型中的 NodeList
介面。)
3.3.1. 基本自定義¶
- object.__new__(cls[, ...])¶
呼叫此方法以建立類 cls 的新例項。
__new__()
是一個靜態方法(特殊情況,因此您不需要將其宣告為靜態方法),它將請求例項的類作為其第一個引數。其餘引數是傳遞給物件建構函式表示式(對類的呼叫)的引數。__new__()
的返回值應該是新的物件例項(通常是 cls 的例項)。典型的實現透過使用適當的引數呼叫超類的
__new__()
方法,並使用super().__new__(cls[, ...])
來建立類的新例項,然後根據需要修改新建立的例項,然後再返回它。如果在物件構造期間呼叫
__new__()
並且它返回 cls 的例項,則將像__init__(self[, ...])
一樣呼叫新例項的__init__()
方法,其中 self 是新例項,其餘引數與傳遞給物件建構函式的引數相同。如果
__new__()
未返回 cls 的例項,則不會呼叫新例項的__init__()
方法。__new__()
主要用於允許不可變型別(例如 int、str 或 tuple)的子類自定義例項的建立。它通常也在自定義元類中被重寫,以自定義類的建立。
- object.__init__(self[, ...])¶
在例項建立之後(透過
__new__()
),但在將其返回給呼叫方之前呼叫此方法。引數是傳遞給類建構函式表示式的引數。如果基類具有__init__()
方法,則派生類的__init__()
方法(如果有)必須顯式呼叫它,以確保例項的基類部分得到正確的初始化;例如:super().__init__([args...])
。因為
__new__()
和__init__()
在構造物件時協同工作(__new__()
建立物件,而__init__()
自定義物件),因此__init__()
不得返回非None
值;這樣做會導致在執行時引發TypeError
。
- object.__del__(self)¶
當例項即將被銷燬時呼叫。這也稱為終結器或(不恰當地)解構函式。如果基類具有
__del__()
方法,則派生類的__del__()
方法(如果存在)必須顯式呼叫它,以確保正確刪除例項的基類部分。可以(但不建議!)透過建立對例項的新引用,使
__del__()
方法推遲例項的銷燬。這稱為物件復活。當一個復活的物件即將被銷燬時,是否會第二次呼叫__del__()
方法取決於具體實現;當前的 CPython 實現只會呼叫一次。不保證當直譯器退出時,仍然存在的物件的
__del__()
方法會被呼叫。weakref.finalize
提供了一種直接的方法來註冊一個清理函式,以便在物件被垃圾回收時呼叫。注意
del x
不會直接呼叫x.__del__()
— 前者將x
的引用計數減 1,而後者只有在x
的引用計數達到零時才會被呼叫。CPython 實現細節: 引用迴圈可能會阻止物件的引用計數變為零。在這種情況下,迴圈稍後會被 迴圈垃圾回收器 檢測並刪除。引用迴圈的一個常見原因是當異常在區域性變數中被捕獲時。幀的區域性變數隨後引用該異常,該異常引用其自己的回溯,而回溯又引用回溯中捕獲的所有幀的區域性變數。
另請參閱
gc
模組的文件。警告
由於呼叫
__del__()
方法的情況很複雜,因此在執行過程中發生的異常會被忽略,並且會在sys.stderr
上列印警告。特別是
- object.__repr__(self)¶
由內建函式
repr()
呼叫,以計算物件的“官方”字串表示形式。如果可能,這應該看起來像一個有效的 Python 表示式,可以用來重新建立具有相同值的物件(給定適當的環境)。如果不可能,則應返回<...一些有用的描述...>
形式的字串。返回值必須是字串物件。如果一個類定義了__repr__()
但沒有定義__str__()
,那麼當需要該類的例項的“非正式”字串表示形式時,也會使用__repr__()
。這通常用於除錯,因此表示形式必須資訊豐富且明確。
object
類本身提供了預設實現。
- object.__str__(self)¶
由
str(object)
、預設的__format__()
實現和內建函式print()
呼叫,以計算物件的“非正式”或漂亮的可列印字串表示形式。返回值必須是 str 物件。此方法與
object.__repr__()
不同之處在於,不期望__str__()
返回有效的 Python 表示式:可以使用更方便或簡潔的表示形式。內建型別
object
定義的預設實現呼叫object.__repr__()
。
- object.__format__(self, format_spec)¶
由內建函式
format()
呼叫,並且透過擴充套件,由 格式化字串字面量 和str.format()
方法的計算呼叫,以生成物件的“格式化”字串表示形式。format_spec 引數是一個字串,其中包含所需格式選項的描述。format_spec 引數的解釋取決於實現__format__()
的型別,但是大多數類會將格式化委託給內建型別之一,或者使用類似的格式化選項語法。有關標準格式語法的描述,請參閱格式規範微語言。
返回值必須是字串物件。
object
類的預設實現應該給定一個空的 format_spec 字串。它會委託給__str__()
。在 3.4 版本中變更: 如果傳遞任何非空字串,則
object
本身的 __format__ 方法會引發TypeError
。在 3.7 版本中變更:
object.__format__(x, '')
現在等價於str(x)
而不是format(str(x), '')
。
- object.__lt__(self, other)¶
- object.__le__(self, other)¶
- object.__eq__(self, other)¶
- object.__ne__(self, other)¶
- object.__gt__(self, other)¶
- object.__ge__(self, other)¶
這些是所謂的“富比較”方法。運算子符號和方法名稱之間的對應關係如下:
x<y
呼叫x.__lt__(y)
,x<=y
呼叫x.__le__(y)
,x==y
呼叫x.__eq__(y)
,x!=y
呼叫x.__ne__(y)
,x>y
呼叫x.__gt__(y)
,以及x>=y
呼叫x.__ge__(y)
。如果富比較方法沒有為給定的引數對實現該操作,則可以返回單例
NotImplemented
。按照慣例,對於成功的比較,返回False
和True
。但是,這些方法可以返回任何值,因此,如果比較運算子在布林上下文中(例如,在if
語句的條件中)使用,Python 將對該值呼叫bool()
,以確定結果是真還是假。預設情況下,
object
透過使用is
來實現__eq__()
,在比較為假的情況下返回NotImplemented
:True if x is y else NotImplemented
。對於__ne__()
,預設情況下它會委託給__eq__()
並反轉結果,除非它是NotImplemented
。比較運算子之間或預設實現之間沒有其他隱含的關係;例如,(x<y or x==y)
的真值並不意味著x<=y
。要從單個根操作自動生成排序操作,請參閱functools.total_ordering()
。預設情況下,
object
類提供了與 值比較 一致的實現:相等性根據物件標識進行比較,而順序比較會引發TypeError
。每個預設方法可以直接生成這些結果,但也可能返回NotImplemented
。有關建立支援自定義比較操作並可用作字典鍵的 可雜湊 物件的一些重要說明,請參閱關於
__hash__()
的段落。這些方法沒有交換引數的版本(在左引數不支援該操作但右引數支援時使用);相反,
__lt__()
和__gt__()
互為反射,__le__()
和__ge__()
互為反射,以及__eq__()
和__ne__()
是它們自己的反射。如果運算元的型別不同,並且右運算元的型別是左運算元型別的直接或間接子類,則右運算元的反射方法具有優先順序,否則左運算元的方法具有優先順序。不考慮虛擬子類化。當沒有合適的方法返回除
NotImplemented
以外的任何值時,==
和!=
運算子將分別回退到is
和is not
。
- object.__hash__(self)¶
由內建函式
hash()
以及對雜湊集合的成員的操作(包括set
、frozenset
和dict
)呼叫。__hash__()
方法應返回一個整數。唯一需要的屬性是,比較相等的物件具有相同的雜湊值;建議將物件的元件的雜湊值混合在一起,這些元件在物件的比較中也起作用,方法是將它們打包成一個元組並對該元組進行雜湊。示例def __hash__(self): return hash((self.name, self.nick, self.color))
注意
hash()
將從物件的自定義__hash__()
方法返回的值截斷為Py_ssize_t
的大小。這在 64 位構建中通常為 8 個位元組,在 32 位構建中通常為 4 個位元組。如果物件的__hash__()
必須在不同位大小的構建上進行互操作,請務必檢查所有受支援構建的寬度。一種簡單的方法是使用python -c "import sys; print(sys.hash_info.width)"
。如果一個類沒有定義
__eq__()
方法,那麼它也不應該定義__hash__()
操作;如果它定義了__eq__()
但沒有定義__hash__()
,它的例項將不能用作可雜湊集合中的元素。如果一個類定義了可變物件並實現了__eq__()
方法,它就不應該實現__hash__()
,因為 可雜湊 集合的實現要求鍵的雜湊值是不可變的(如果物件的雜湊值發生變化,它將會處於錯誤的雜湊桶中)。使用者定義的類預設具有
__eq__()
和__hash__()
方法(繼承自object
類);有了它們,所有物件都會被認為是不相等的(除了它們自身),並且x.__hash__()
會返回一個合適的值,使得x == y
意味著x is y
和hash(x) == hash(y)
兩者都成立。一個重寫了
__eq__()
但沒有定義__hash__()
的類,它的__hash__()
將被隱式設定為None
。當一個類的__hash__()
方法為None
時,當程式嘗試檢索它們的雜湊值時,該類的例項將會引發相應的TypeError
,並且在檢查isinstance(obj, collections.abc.Hashable)
時也會被正確地識別為不可雜湊。如果一個重寫了
__eq__()
的類需要保留父類的__hash__()
實現,則必須透過設定__hash__ = <ParentClass>.__hash__
來顯式告知直譯器。如果一個沒有重寫
__eq__()
的類希望抑制雜湊支援,它應該在類定義中包含__hash__ = None
。一個定義了自己的__hash__()
並顯式引發TypeError
的類,會被isinstance(obj, collections.abc.Hashable)
呼叫錯誤地識別為可雜湊的。注意
預設情況下,str 和 bytes 物件的
__hash__()
值會使用一個不可預測的隨機值進行“加鹽”。雖然它們在單個 Python 程序中保持不變,但它們在 Python 的重複呼叫之間是不可預測的。這旨在提供保護,防止由於精心選擇的輸入利用了字典插入的最壞情況效能,即 O(n2) 複雜度而導致的拒絕服務。有關詳細資訊,請參閱 http://ocert.org/advisories/ocert-2011-003.html。
更改雜湊值會影響集合的迭代順序。Python 從未對這種排序做出保證(並且它通常在 32 位和 64 位版本之間有所不同)。
另請參閱
PYTHONHASHSEED
。在 3.3 版本中更改: 預設啟用雜湊隨機化。
3.3.2. 自定義屬性訪問¶
可以定義以下方法來自定義類例項的屬性訪問(使用、賦值或刪除 x.name
)的含義。
- object.__getattr__(self, name)¶
當預設屬性訪問失敗並引發
AttributeError
時呼叫(要麼是__getattribute__()
引發了AttributeError
,因為 name 不是self
的例項屬性或類樹中的屬性;要麼是 name 屬性的__get__()
引發了AttributeError
)。此方法應該返回(計算出的)屬性值或引發AttributeError
異常。object
類本身不提供此方法。請注意,如果透過正常機制找到了屬性,則不會呼叫
__getattr__()
。(這是__getattr__()
和__setattr__()
之間有意的不對稱。)這樣做既是出於效率方面的考慮,也是因為否則__getattr__()
將無法訪問例項的其他屬性。請注意,至少對於例項變數,您可以透過不在例項屬性字典中插入任何值(而是將它們插入到另一個物件中)來實現完全控制。請參閱下面的__getattribute__()
方法,以瞭解如何真正地完全控制屬性訪問。
- object.__getattribute__(self, name)¶
無條件呼叫,以實現類的例項的屬性訪問。如果該類還定義了
__getattr__()
,則除非__getattribute__()
顯式呼叫它或引發AttributeError
,否則不會呼叫後者。此方法應返回(計算後的)屬性值,或引發AttributeError
異常。為了避免此方法中的無限遞迴,其實現應始終呼叫具有相同名稱的基類方法來訪問它需要的任何屬性,例如,object.__getattribute__(self, name)
。對於某些敏感的屬性訪問,會引發帶有引數
obj
和name
的 審計事件object.__getattr__
。
- object.__setattr__(self, name, value)¶
當嘗試屬性賦值時呼叫。這將代替正常機制(即,將值儲存在例項字典中)被呼叫。name 是屬性名稱,value 是要分配給它的值。
如果
__setattr__()
想要分配給例項屬性,它應該呼叫具有相同名稱的基類方法,例如,object.__setattr__(self, name, value)
。對於某些敏感的屬性賦值,會引發帶有引數
obj
、name
、value
的 審計事件object.__setattr__
。
- object.__delattr__(self, name)¶
類似於
__setattr__()
,但用於屬性刪除而不是賦值。只有當del obj.name
對該物件有意義時才應實現此方法。對於某些敏感的屬性刪除,會引發帶有引數
obj
和name
的 審計事件object.__delattr__
。
3.3.2.1. 自定義模組屬性訪問¶
特殊名稱 __getattr__
和 __dir__
也可用於自定義對模組屬性的訪問。模組級別的 __getattr__
函式應接受一個引數,該引數是屬性的名稱,並返回計算後的值或引發 AttributeError
。如果透過正常查詢(即 object.__getattribute__()
)在模組物件上找不到屬性,則在引發 AttributeError
之前,會在模組的 __dict__
中搜索 __getattr__
。如果找到,則會使用屬性名稱呼叫它並返回結果。
__dir__
函式應不接受任何引數,並返回一個字串的可迭代物件,該字串表示模組上可訪問的名稱。如果存在,則此函式會覆蓋模組上的標準 dir()
搜尋。
為了更精細地自定義模組行為(設定屬性、屬性等),可以將模組物件的 __class__
屬性設定為 types.ModuleType
的子類。例如
import sys
from types import ModuleType
class VerboseModule(ModuleType):
def __repr__(self):
return f'Verbose {self.__name__}'
def __setattr__(self, attr, value):
print(f'Setting {attr}...')
super().__setattr__(attr, value)
sys.modules[__name__].__class__ = VerboseModule
注意
定義模組 __getattr__
和設定模組 __class__
僅影響使用屬性訪問語法進行的查詢 – 直接訪問模組全域性變數(無論是透過模組內的程式碼,還是透過對模組全域性變數字典的引用)不受影響。
在 3.5 版本中更改:__class__
模組屬性現在是可寫的。
在 3.7 版本中新增:__getattr__
和 __dir__
模組屬性。
另請參閱
- PEP 562 - 模組 __getattr__ 和 __dir__
描述模組上的
__getattr__
和__dir__
函式。
3.3.2.2. 實現描述符¶
以下方法僅當包含該方法的類的例項(所謂的描述符類)出現在所有者類中時才適用(描述符必須位於所有者的類字典中或其父級之一的類字典中)。在下面的示例中,“屬性”指的是其名稱是所有者類的 __dict__
中屬性的鍵的屬性。object
類本身不實現這些協議中的任何一個。
- object.__get__(self, instance, owner=None)¶
呼叫以獲取所有者類的屬性(類屬性訪問)或該類的例項的屬性(例項屬性訪問)。可選的 owner 引數是所有者類,而 instance 是透過其訪問屬性的例項,或者當透過 owner 訪問屬性時為
None
。此方法應返回計算後的屬性值或引發
AttributeError
異常。PEP 252 指定
__get__()
可使用一個或兩個引數呼叫。Python 自己的內建描述符支援此規範;但是,某些第三方工具可能具有需要兩個引數的描述符。Python 自己的__getattribute__()
實現始終傳遞兩個引數,無論它們是否是必需的。
- object.__set__(self, instance, value)¶
呼叫以將所有者類的例項 instance 上的屬性設定為新值 value。
請注意,新增
__set__()
或__delete__()
會將描述符的型別更改為“資料描述符”。有關更多詳細資訊,請參閱 呼叫描述符。
- object.__delete__(self, instance)¶
呼叫以刪除所有者類的例項 instance 上的屬性。
描述符的例項也可能存在 __objclass__
屬性
3.3.2.3. 呼叫描述器¶
一般來說,描述器是一個具有“繫結行為”的物件屬性,它的屬性訪問已被描述器協議中的方法覆蓋:__get__()
、 __set__()
和 __delete__()
。如果為某個物件定義了這些方法中的任何一個,則稱其為描述器。
屬性訪問的預設行為是從物件的字典中獲取、設定或刪除屬性。例如,a.x
的查詢鏈從 a.__dict__['x']
開始,然後是 type(a).__dict__['x']
,並繼續遍歷 type(a)
的基類,不包括元類。
但是,如果查詢的值是一個定義了描述器方法的物件,那麼 Python 可能會覆蓋預設行為並呼叫描述器方法。這在優先順序鏈中發生的位置取決於定義了哪些描述器方法以及它們是如何被呼叫的。
描述器呼叫的起始點是一個繫結,a.x
。引數如何組裝取決於 a
。
- 直接呼叫
最簡單和最不常見的呼叫是當用戶程式碼直接呼叫描述器方法時:
x.__get__(a)
。- 例項繫結
如果繫結到物件例項,
a.x
將轉換為以下呼叫:type(a).__dict__['x'].__get__(a, type(a))
。- 類繫結
如果繫結到類,
A.x
將轉換為以下呼叫:A.__dict__['x'].__get__(None, A)
。- 超類繫結
諸如
super(A, a).x
的點查詢會在a.__class__.__mro__
中搜索跟隨A
的基類B
,然後返回B.__dict__['x'].__get__(a, A)
。如果不是描述器,則x
將保持不變地返回。
對於例項繫結,描述器呼叫的優先順序取決於定義了哪些描述器方法。一個描述器可以定義 __get__()
、 __set__()
和 __delete__()
的任意組合。如果它沒有定義 __get__()
,那麼除非物件的例項字典中有一個值,否則訪問該屬性將返回描述器物件本身。如果描述器定義了 __set__()
和/或 __delete__()
,則它是一個數據描述器;如果它都沒有定義,則它是一個非資料描述器。通常,資料描述器定義了 __get__()
和 __set__()
,而非資料描述器只有 __get__()
方法。定義了 __get__()
和 __set__()
(和/或 __delete__()
) 的資料描述器總是會覆蓋例項字典中的重新定義。相反,非資料描述器可以被例項覆蓋。
Python 方法(包括用 @staticmethod
和 @classmethod
修飾的方法)被實現為非資料描述器。因此,例項可以重新定義和覆蓋方法。這允許各個例項獲得與同一類的其他例項不同的行為。
property()
函式被實現為資料描述器。因此,例項無法覆蓋屬性的行為。
3.3.2.4. __slots__¶
__slots__ 允許我們顯式宣告資料成員(如屬性),並禁止建立 __dict__
和 __weakref__ (除非在 __slots__ 中顯式宣告或在父類中可用。)
與使用 __dict__
相比,節省的空間可能非常顯著。屬性查詢速度也可以顯著提高。
- object.__slots__¶
這個類變數可以被賦值為一個字串、可迭代物件或帶有例項使用的變數名的字串序列。__slots__ 為宣告的變數保留空間,並阻止為每個例項自動建立
__dict__
和 __weakref__。
關於使用 __slots__ 的注意事項
當從沒有 __slots__ 的類繼承時,例項的
__dict__
和 __weakref__ 屬性始終可訪問。如果沒有
__dict__
變數,例項無法被分配未在 __slots__ 定義中列出的新變數。嘗試分配給未列出的變數名會引發AttributeError
。如果需要動態分配新變數,請將'__dict__'
新增到 __slots__ 宣告中的字串序列中。如果沒有每個例項的 __weakref__ 變數,則定義 __slots__ 的類不支援對其例項的
弱引用
。如果需要弱引用支援,請將'__weakref__'
新增到 __slots__ 宣告中的字串序列中。__slots__ 在類級別透過為每個變數名建立 描述符 來實現。因此,類屬性不能用於為 __slots__ 定義的例項變數設定預設值;否則,類屬性將覆蓋描述符的賦值。
__slots__ 宣告的作用不限於它定義的類。在父類中宣告的 __slots__ 在子類中可用。但是,子類的例項將獲得
__dict__
和 __weakref__,除非子類也定義了 __slots__ (其中應僅包含任何額外槽的名稱)。如果一個類定義了一個基類中也定義的槽,則由基類槽定義的例項變數不可訪問(除非直接從基類檢索其描述符)。這使得程式的含義未定義。將來可能會新增一個檢查以防止這種情況。
如果為從
“可變長度” 內建型別
(例如int
、bytes
和tuple
)派生的類定義了非空的 __slots__,則會引發TypeError
。任何非字串 可迭代物件 都可以賦值給 __slots__。
如果使用
字典
來賦值 __slots__,則字典的鍵將用作槽名。字典的值可以用於提供每個屬性的文件字串,這些文件字串將被inspect.getdoc()
識別,並顯示在help()
的輸出中。__class__
賦值僅在兩個類具有相同的 __slots__ 時才有效。可以使用具有多個帶槽父類的多重繼承,但只允許一個父類擁有由槽建立的屬性(其他基類必須具有空的槽佈局) - 違規會引發
TypeError
。如果 __slots__ 使用了迭代器,則會為迭代器的每個值建立一個描述符。但是,__slots__ 屬性將是一個空的迭代器。
3.3.3. 自定義類建立¶
每當一個類繼承自另一個類時,都會在父類上呼叫 __init_subclass__()
。 這樣,就可以編寫改變子類行為的類。這與類裝飾器密切相關,但類裝飾器隻影響它們所應用的特定類,而 __init_subclass__
僅適用於定義該方法的類的未來子類。
- classmethod object.__init_subclass__(cls)¶
每當包含的類被子類化時,都會呼叫此方法。cls 然後是新的子類。 如果定義為普通的例項方法,則此方法將隱式轉換為類方法。
傳遞給新類的關鍵字引數會傳遞給父類的
__init_subclass__
。 為了與其他使用__init_subclass__
的類相容,應該取出需要的關鍵字引數,並將其他引數傳遞給基類,如下所示class Philosopher: def __init_subclass__(cls, /, default_name, **kwargs): super().__init_subclass__(**kwargs) cls.default_name = default_name class AustralianPhilosopher(Philosopher, default_name="Bruce"): pass
預設實現
object.__init_subclass__
不執行任何操作,但如果使用任何引數呼叫它,則會引發錯誤。注意
元類提示
metaclass
由型別機制的其餘部分使用,並且永遠不會傳遞給__init_subclass__
實現。實際的元類(而不是顯式提示)可以作為type(cls)
訪問。在 3.6 版本中新增。
建立類時,type.__new__()
會掃描類變數,並回調到具有 __set_name__()
鉤子的那些變數。
- object.__set_name__(self, owner, name)¶
在建立所有者類 owner 時自動呼叫。該物件已在該類中分配給 name
class A: x = C() # Automatically calls: x.__set_name__(A, 'x')
如果在建立類後分配類變數,則不會自動呼叫
__set_name__()
。 如果需要,可以直接呼叫__set_name__()
。class A: pass c = C() A.x = c # The hook is not called c.__set_name__(A, 'x') # Manually invoke the hook
有關更多詳細資訊,請參閱 建立類物件。
在 3.6 版本中新增。
3.3.3.1. 元類¶
預設情況下,類是使用 type()
構造的。 類主體在一個新的名稱空間中執行,並且類名稱在本地繫結到 type(name, bases, namespace)
的結果。
可以透過在類定義行中傳遞 metaclass
關鍵字引數,或透過繼承包含此類引數的現有類來自定義類建立過程。在以下示例中,MyClass
和 MySubclass
都是 Meta
的例項
class Meta(type):
pass
class MyClass(metaclass=Meta):
pass
class MySubclass(MyClass):
pass
在類定義中指定的任何其他關鍵字引數都會傳遞給下面描述的所有元類操作。
當執行類定義時,會發生以下步驟
解析 MRO 條目;
確定適當的元類;
準備類名稱空間;
執行類主體;
建立類物件。
3.3.3.2. 解析 MRO 條目¶
- object.__mro_entries__(self, bases)¶
如果出現在類定義中的基類不是
type
的例項,則會在基類上搜索__mro_entries__()
方法。 如果找到__mro_entries__()
方法,則在建立類時,將基類替換為對__mro_entries__()
的呼叫的結果。 該方法使用傳遞給 bases 引數的原始基類元組進行呼叫,並且必須返回一個類元組,該元組將代替基類使用。 返回的元組可以為空:在這些情況下,將忽略原始基類。
另請參閱
types.resolve_bases()
動態解析不是
type
例項的基類。types.get_original_bases()
檢索在
__mro_entries__()
修改之前類的“原始基類”。- PEP 560
對 typing 模組和泛型型別的核心支援。
3.3.3.3. 確定適當的元類¶
確定類定義的適當元類,如下所示
如果沒有給出基類和顯式元類,則使用
type()
;如果給出了顯式元類,並且它不是
type()
的例項,則直接將其用作元類;如果
type()
的例項作為顯式元類給出,或者定義了基類,則使用最派生的元類。
最派生的元類選自顯式指定的元類(如果有)和所有指定基類的元類(即 type(cls)
)。 最派生的元類是 所有 這些候選元類的子型別。 如果沒有候選元類滿足該標準,則類定義將失敗並顯示 TypeError
。
3.3.3.4. 準備類名稱空間¶
一旦確定了適當的元類,就會準備類名稱空間。 如果元類具有 __prepare__
屬性,則將其作為 namespace = metaclass.__prepare__(name, bases, **kwds)
呼叫(其中,任何額外的關鍵字引數都來自類定義)。 __prepare__
方法應作為 classmethod
實現。 __prepare__
返回的名稱空間會傳遞給 __new__
,但是當建立最終的類物件時,該名稱空間會被複制到一個新的 dict
中。
如果元類沒有 __prepare__
屬性,則類名稱空間將初始化為空的有序對映。
另請參閱
- PEP 3115 - Python 3000 中的元類
引入了
__prepare__
名稱空間鉤子
3.3.3.5. 執行類主體¶
類主體的執行(大致)為 exec(body, globals(), namespace)
。 與正常呼叫 exec()
的主要區別在於,詞法作用域允許類主體(包括任何方法)在類定義發生在函式內部時引用當前和外部範圍內的名稱。
但是,即使類定義發生在函式內部,在類內部定義的方法仍然無法看到在類範圍內定義的名稱。 必須透過例項或類方法的第一個引數,或者透過下一節中描述的隱式詞法作用域 __class__
引用來訪問類變數。
3.3.3.6. 建立類物件¶
透過執行類主體填充類名稱空間後,透過呼叫 metaclass(name, bases, namespace, **kwds)
建立類物件(此處傳遞的附加關鍵字與傳遞給 __prepare__
的關鍵字相同)。
這個類物件將被 super()
的零引數形式引用。__class__
是由編譯器建立的隱式閉包引用,如果類體中的任何方法引用 __class__
或 super
。這使得 super()
的零引數形式能夠根據詞法作用域正確識別正在定義的類,而用於進行當前呼叫的類或例項則根據傳遞給方法的第一個引數來識別。
CPython 實現細節: 在 CPython 3.6 及更高版本中,__class__
單元格作為類名稱空間中的 __classcell__
條目傳遞給元類。如果存在,則必須將其傳播到 type.__new__
呼叫,以便正確初始化類。否則,在 Python 3.8 中將導致 RuntimeError
。
當使用預設元類 type
或最終呼叫 type.__new__
的任何元類時,在建立類物件後,將呼叫以下額外的自定義步驟:
type.__new__
方法收集類名稱空間中定義了__set_name__()
方法的所有屬性;這些
__set_name__
方法會被呼叫,並傳入正在定義的類和該特定屬性的分配名稱;新類的直接父類在其方法解析順序中呼叫
__init_subclass__()
鉤子。
建立類物件後,它會被傳遞給類定義中包含的類裝飾器(如果有),並且結果物件會作為定義的類繫結到區域性名稱空間中。
當 type.__new__
建立一個新類時,作為名稱空間引數提供的物件會被複制到一個新的有序對映中,並且原始物件會被丟棄。新的副本會包裝在一個只讀代理中,該代理會成為類物件的 __dict__
屬性。
另請參閱
- PEP 3135 - 新的 super
描述隱式的
__class__
閉包引用
3.3.3.7. 元類的用途¶
元類的潛在用途是無限的。一些已被探索的想法包括列舉、日誌記錄、介面檢查、自動委託、自動屬性建立、代理、框架以及自動資源鎖定/同步。
3.3.4. 自定義例項和子類檢查¶
以下方法用於覆蓋內建函式 isinstance()
和 issubclass()
的預設行為。
特別是,元類 abc.ABCMeta
實現了這些方法,以便允許將抽象基類(ABC)作為“虛擬基類”新增到任何類或型別(包括內建型別),包括其他 ABC。
- type.__instancecheck__(self, instance)¶
如果 instance 應被視為 class 的(直接或間接)例項,則返回 true。如果已定義,則呼叫它來實現
isinstance(instance, class)
。
- type.__subclasscheck__(self, subclass)¶
如果 subclass 應被視為 class 的(直接或間接)子類,則返回 true。如果已定義,則呼叫它來實現
issubclass(subclass, class)
。
請注意,這些方法在類的型別(元類)上查詢。它們不能在實際類中定義為類方法。這與在例項上呼叫的特殊方法的查詢一致,只是在這種情況下,例項本身就是一個類。
另請參閱
- PEP 3119 - 引入抽象基類
包括透過
__instancecheck__()
和__subclasscheck__()
自定義isinstance()
和issubclass()
行為的規範,以及在將抽象基類(請參閱abc
模組)新增到語言的背景下對此功能的需求。
3.3.5. 模擬泛型型別¶
當使用 型別註解 時,使用 Python 的方括號表示法引數化 泛型型別 通常很有用。例如,註解 list[int]
可用於表示一個 list
,其中所有元素都是 int
型別。
另請參閱
- PEP 484 - 型別提示
介紹 Python 的型別註解框架
- 泛型別名型別
表示引數化泛型類的物件的文件
- 泛型、使用者定義的泛型 和
typing.Generic
關於如何實現可在執行時引數化並可被靜態型別檢查器理解的泛型類的文件。
一個類通常只有在定義了特殊的類方法 __class_getitem__()
時才能被引數化。
- classmethod object.__class_getitem__(cls, key)¶
返回一個物件,表示由 key 中找到的型別引數對泛型類的特化。
當在類上定義時,
__class_getitem__()
自動成為類方法。因此,在定義它時,無需使用@classmethod
進行裝飾。
3.3.5.1. __class_getitem__ 的目的¶
__class_getitem__()
的目的是允許執行時引數化標準庫泛型類,以便更輕鬆地將 型別提示 應用於這些類。
要實現可以在執行時引數化並可被靜態型別檢查器理解的自定義泛型類,使用者應該從已經實現 __class_getitem__()
的標準庫類繼承,或者從 typing.Generic
繼承,它有自己的 __class_getitem__()
實現。
在標準庫之外定義的類上自定義實現 __class_getitem__()
可能不會被 mypy 等第三方型別檢查器理解。不鼓勵在任何類上使用 __class_getitem__()
用於型別提示以外的目的。
3.3.5.2. __class_getitem__ 與 __getitem__¶
通常,使用方括號對物件進行下標操作會呼叫物件類中定義的 __getitem__()
例項方法。但是,如果被下標操作的物件本身是一個類,則可能會呼叫類方法 __class_getitem__()
。如果定義正確,__class_getitem__()
應該返回一個 GenericAlias 物件。
對於表示式 obj[x]
,Python 直譯器會遵循類似以下的過程來決定是呼叫 __getitem__()
還是 __class_getitem__()
from inspect import isclass
def subscribe(obj, x):
"""Return the result of the expression 'obj[x]'"""
class_of_obj = type(obj)
# If the class of obj defines __getitem__,
# call class_of_obj.__getitem__(obj, x)
if hasattr(class_of_obj, '__getitem__'):
return class_of_obj.__getitem__(obj, x)
# Else, if obj is a class and defines __class_getitem__,
# call obj.__class_getitem__(x)
elif isclass(obj) and hasattr(obj, '__class_getitem__'):
return obj.__class_getitem__(x)
# Else, raise an exception
else:
raise TypeError(
f"'{class_of_obj.__name__}' object is not subscriptable"
)
在 Python 中,所有類本身都是其他類的例項。一個類的類被稱為該類的元類,大多數類的元類都是 type
類。type
沒有定義 __getitem__()
,這意味著諸如 list[int]
、dict[str, float]
和 tuple[str, bytes]
等表示式都會導致呼叫 __class_getitem__()
>>> # list has class "type" as its metaclass, like most classes:
>>> type(list)
<class 'type'>
>>> type(dict) == type(list) == type(tuple) == type(str) == type(bytes)
True
>>> # "list[int]" calls "list.__class_getitem__(int)"
>>> list[int]
list[int]
>>> # list.__class_getitem__ returns a GenericAlias object:
>>> type(list[int])
<class 'types.GenericAlias'>
但是,如果一個類有一個定義了 __getitem__()
的自定義元類,則對該類進行下標操作可能會導致不同的行為。在 enum
模組中可以找到一個這樣的例子
>>> from enum import Enum
>>> class Menu(Enum):
... """A breakfast menu"""
... SPAM = 'spam'
... BACON = 'bacon'
...
>>> # Enum classes have a custom metaclass:
>>> type(Menu)
<class 'enum.EnumMeta'>
>>> # EnumMeta defines __getitem__,
>>> # so __class_getitem__ is not called,
>>> # and the result is not a GenericAlias object:
>>> Menu['SPAM']
<Menu.SPAM: 'spam'>
>>> type(Menu['SPAM'])
<enum 'Menu'>
另請參閱
- PEP 560 - 對型別模組和泛型型別的核心支援
介紹了
__class_getitem__()
,並概述了當一個下標操作導致呼叫__class_getitem__()
而不是__getitem__()
的情況
3.3.6. 模擬可呼叫物件¶
3.3.7. 模擬容器型別¶
可以定義以下方法來實現容器物件。object
類本身不提供任何這些方法。容器通常是序列(例如 列表
或 元組
)或對映(如字典),但也可以表示其他容器。第一組方法用於模擬序列或對映;區別在於,對於序列,允許的鍵應該是整數 *k*,其中 0 <= k < N
,其中 *N* 是序列的長度,或者 切片
物件,它定義了專案範圍。還建議對映提供 keys()
、values()
、items()
、get()
、clear()
、setdefault()
、pop()
、popitem()
、copy()
和 update()
方法,其行為類似於 Python 標準 字典
物件。collections.abc
模組提供了一個 MutableMapping
抽象基類,以幫助從 __getitem__()
、__setitem__()
、__delitem__()
和 keys()
的基本集合中建立這些方法。可變序列應該提供 append()
、count()
、index()
、extend()
、insert()
、pop()
、remove()
、reverse()
和 sort()
方法,類似於 Python 標準 列表
物件。最後,序列型別應該透過定義 __add__()
、__radd__()
、__iadd__()
、__mul__()
、__rmul__()
和 __imul__()
方法來實現加法(表示連線)和乘法(表示重複),如下所述;它們不應該定義其他數值運算子。建議對映和序列都實現 __contains__()
方法,以允許有效使用 in
運算子;對於對映,in
應該搜尋對映的鍵;對於序列,它應該搜尋值。進一步建議對映和序列都實現 __iter__()
方法,以允許有效地迭代容器;對於對映,__iter__()
應該迭代物件的鍵;對於序列,它應該迭代值。
- object.__len__(self)¶
呼叫以實現內建函式
len()
。應返回物件的長度,一個大於等於 0 的整數。此外,一個沒有定義__bool__()
方法且其__len__()
方法返回零的物件在布林上下文中被認為是假的。CPython 實現細節: 在 CPython 中,長度必須最多為
sys.maxsize
。如果長度大於sys.maxsize
,某些功能(例如len()
)可能會引發OverflowError
。為了防止真值測試引發OverflowError
,物件必須定義一個__bool__()
方法。
- object.__length_hint__(self)¶
呼叫此方法以實現
operator.length_hint()
。應返回物件的估計長度(可能大於或小於實際長度)。長度必須是大於等於 0 的整數。返回值也可以是NotImplemented
,這被視為__length_hint__
方法根本不存在的情況。此方法純粹是一種最佳化,對於正確性而言並非必需。3.4 版本新增。
注意
切片完全透過以下三個方法完成。像這樣的呼叫
a[1:2] = b
被轉換為
a[slice(1, 2, None)] = b
依此類推。缺失的切片項始終用 None
填充。
- object.__getitem__(self, key)¶
呼叫此方法以實現
self[key]
的求值。對於序列型別,接受的鍵應該是整數。可選地,它們也可以支援slice
物件。負索引支援也是可選的。如果 *key* 是不適當的型別,則可能會引發TypeError
;如果 *key* 是序列索引集之外的值(在對負值進行任何特殊解釋之後),則應引發IndexError
。對於對映型別,如果 *key* 缺失(不在容器中),則應引發KeyError
。注意
for
迴圈期望對非法索引引發IndexError
,以便正確檢測序列的結尾。注意
當下標訪問一個*類*時,可能會呼叫特殊的類方法
__class_getitem__()
,而不是__getitem__()
。有關更多詳細資訊,請參見__class_getitem__ 與 __getitem__。
- object.__setitem__(self, key, value)¶
呼叫此方法以實現對
self[key]
的賦值。與__getitem__()
的註釋相同。僅當物件支援更改鍵的值,或者可以新增新鍵時,才應為對映實現此方法;如果可以替換元素,則應為序列實現此方法。對於不正確的 *key* 值,應引發與__getitem__()
方法相同的異常。
- object.__delitem__(self, key)¶
呼叫此方法以實現刪除
self[key]
。與__getitem__()
的註釋相同。僅當物件支援刪除鍵時,才應為對映實現此方法;如果可以從序列中刪除元素,則應為序列實現此方法。對於不正確的 *key* 值,應引發與__getitem__()
方法相同的異常。
- object.__missing__(self, key)¶
當鍵不在字典中時,
dict
.__getitem__()
呼叫此方法來實現 dict 子類的self[key]
。
- object.__reversed__(self)¶
當
reversed()
內建函式(如果存在)呼叫此方法來實現反向迭代。它應返回一個新的迭代器物件,該物件以相反的順序迭代容器中的所有物件。如果未提供
__reversed__()
方法,則reversed()
內建函式將回退到使用序列協議(__len__()
和__getitem__()
)。支援序列協議的物件僅當可以提供比reversed()
提供的實現更高效的實現時,才應提供__reversed__()
。
成員資格測試運算子(in
和 not in
)通常透過迭代容器來實現。但是,容器物件可以提供以下特殊方法來使用更高效的實現,而無需物件可迭代。
- object.__contains__(self, item)¶
呼叫以實現成員測試運算子。如果item在self中,則應返回 true,否則返回 false。對於對映物件,這應考慮對映的鍵,而不是值或鍵值對。
對於沒有定義
__contains__()
的物件,成員測試首先嚐試透過__iter__()
進行迭代,然後透過__getitem__()
進行舊的序列迭代協議,請參閱語言參考中的此部分。
3.3.8. 模擬數字型別¶
可以定義以下方法來模擬數字物件。對於特定型別的數字不支援的操作(例如,非整數的位運算),應保留未定義的方法。
- object.__add__(self, other)¶
- object.__sub__(self, other)¶
- object.__mul__(self, other)¶
- object.__matmul__(self, other)¶
- object.__truediv__(self, other)¶
- object.__floordiv__(self, other)¶
- object.__mod__(self, other)¶
- object.__divmod__(self, other)¶
- object.__pow__(self, other[, modulo])¶
- object.__lshift__(self, other)¶
- object.__rshift__(self, other)¶
- object.__and__(self, other)¶
- object.__xor__(self, other)¶
- object.__or__(self, other)¶
這些方法被呼叫以實現二進位制算術運算(
+
、-
、*
、@
、/
、//
、%
、divmod()
、pow()
、**
、<<
、>>
、&
、^
、|
)。例如,要計算表示式x + y
,其中 x 是具有__add__()
方法的類的例項,則會呼叫type(x).__add__(x, y)
。__divmod__()
方法應該等效於使用__floordiv__()
和__mod__()
;它不應該與__truediv__()
相關。請注意,如果內建pow()
函式的三元版本要被支援,則__pow__()
應該定義為接受可選的第三個引數。如果這些方法之一不支援使用提供的引數進行操作,則應返回
NotImplemented
。
- object.__radd__(self, other)¶
- object.__rsub__(self, other)¶
- object.__rmul__(self, other)¶
- object.__rmatmul__(self, other)¶
- object.__rtruediv__(self, other)¶
- object.__rfloordiv__(self, other)¶
- object.__rmod__(self, other)¶
- object.__rdivmod__(self, other)¶
- object.__rpow__(self, other[, modulo])¶
- object.__rlshift__(self, other)¶
- object.__rrshift__(self, other)¶
- object.__rand__(self, other)¶
- object.__rxor__(self, other)¶
- object.__ror__(self, other)¶
這些方法被呼叫以實現反射(交換)運算元的二進位制算術運算 (
+
,-
,*
,@
,/
,//
,%
,divmod()
,pow()
,**
,<<
,>>
,&
,^
,|
)。 只有在左運算元不支援相應的操作[3] 且運算元的型別不同時才會呼叫這些函式。[4] 例如,為了計算表示式x - y
,其中 *y* 是一個具有__rsub__()
方法的類的例項,如果type(x).__sub__(x, y)
返回NotImplemented
,則呼叫type(y).__rsub__(y, x)
。請注意,三元
pow()
不會嘗試呼叫__rpow__()
(強制規則會變得過於複雜)。注意
如果右運算元的型別是左運算元型別的子類,並且該子類為該操作提供了反射方法的不同實現,則會在呼叫左運算元的非反射方法之前呼叫此方法。此行為允許子類覆蓋其祖先的操作。
- object.__iadd__(self, other)¶
- object.__isub__(self, other)¶
- object.__imul__(self, other)¶
- object.__imatmul__(self, other)¶
- object.__itruediv__(self, other)¶
- object.__ifloordiv__(self, other)¶
- object.__imod__(self, other)¶
- object.__ipow__(self, other[, modulo])¶
- object.__ilshift__(self, other)¶
- object.__irshift__(self, other)¶
- object.__iand__(self, other)¶
- object.__ixor__(self, other)¶
- object.__ior__(self, other)¶
這些方法被呼叫以實現增強的算術賦值(
+=
,-=
,*=
,@=
,/=
,//=
,%=
,**=
,<<=
,>>=
,&=
,^=
,|=
)。 這些方法應嘗試就地執行操作(修改 *self*)並返回結果(可以是,但不必是 *self*)。 如果未定義特定方法,或者該方法返回NotImplemented
,則增強賦值將回退到正常方法。 例如,如果 *x* 是具有__iadd__()
方法的類的例項,則x += y
等效於x = x.__iadd__(y)
。 如果__iadd__()
不存在,或者x.__iadd__(y)
返回NotImplemented
,則會像評估x + y
一樣考慮x.__add__(y)
和y.__radd__(x)
。 在某些情況下,增強賦值可能會導致意外錯誤(請參閱 為什麼當加法有效時,a_tuple[i] += [‘item’] 會引發異常?),但此行為實際上是資料模型的一部分。
- object.__neg__(self)¶
- object.__pos__(self)¶
- object.__abs__(self)¶
- object.__invert__(self)¶
呼叫此方法以實現一元算術運算(
-
,+
,abs()
和~
)。
- object.__index__(self)¶
呼叫此方法以實現
operator.index()
,並且在 Python 需要將數字物件無損轉換為整數物件時(例如在切片中,或在內建的bin()
、hex()
和oct()
函式中)。此方法的存在表示數字物件是整數型別。必須返回一個整數。如果未定義
__int__()
,__float__()
和__complex__()
,則相應的內建函式int()
,float()
和complex()
將回退到__index__()
。
- object.__round__(self[, ndigits])¶
- object.__trunc__(self)¶
- object.__floor__(self)¶
- object.__ceil__(self)¶
呼叫此方法以實現內建函式
round()
和math
函式trunc()
,floor()
和ceil()
。除非將 ndigits 傳遞給__round__()
,否則所有這些方法都應返回截斷為Integral
(通常是int
) 的物件的值。如果既未定義
__int__()
也未定義__index__()
,則內建函式int()
將回退到__trunc__()
。在 3.11 版本中更改: 將
int()
委託給__trunc__()
已被棄用。
3.3.9. with 語句上下文管理器¶
上下文管理器 是一個物件,它定義了執行 with
語句時要建立的執行時上下文。 上下文管理器處理程式碼塊執行的所需執行時上下文的進入和退出。上下文管理器通常使用 with
語句(在 with 語句 部分中描述)呼叫,但也可以透過直接呼叫它們的方法來使用。
上下文管理器的典型用途包括儲存和恢復各種全域性狀態、鎖定和解鎖資源、關閉開啟的檔案等。
有關上下文管理器的更多資訊,請參見 上下文管理器型別。 object
類本身不提供上下文管理器方法。
- object.__exit__(self, exc_type, exc_value, traceback)¶
退出與此物件相關的執行時上下文。 這些引數描述了導致上下文退出的異常。 如果上下文在沒有異常的情況下退出,則所有三個引數都將為
None
。如果提供了異常,並且該方法希望抑制該異常(即,阻止其傳播),則應返回一個真值。 否則,該異常將在退出此方法時正常處理。
請注意,
__exit__()
方法不應重新引發傳入的異常;這是呼叫者的責任。
3.3.10. 自定義類模式匹配中的位置引數¶
在模式中使用類名稱時,預設情況下不允許模式中的位置引數,即 case MyClass(x, y)
通常在 MyClass
中沒有特殊支援的情況下無效。為了能夠使用這種模式,類需要定義一個 __match_args__ 屬性。
- object.__match_args__¶
此類變數可以分配一個字串元組。 當此類在帶有位置引數的類模式中使用時,每個位置引數都將轉換為關鍵字引數,並使用 __match_args__ 中的相應值作為關鍵字。 缺少此屬性等效於將其設定為
()
。
例如,如果 MyClass.__match_args__
是 ("left", "center", "right")
,則意味著 case MyClass(x, y)
等價於 case MyClass(left=x, center=y)
。請注意,模式中的引數數量必須小於或等於 __match_args__ 中的元素數量;如果大於,則模式匹配嘗試將引發 TypeError
。
在 3.10 版本中新增。
另請參閱
- PEP 634 - 結構化模式匹配
Python
match
語句的規範。
3.3.11. 模擬緩衝區型別¶
緩衝區協議 為 Python 物件提供了一種暴露對底層記憶體陣列的高效訪問的方法。此協議由諸如 bytes
和 memoryview
之類的內建型別實現,並且第三方庫可以定義其他緩衝區型別。
雖然緩衝區型別通常用 C 實現,但也可以在 Python 中實現該協議。
- object.__buffer__(self, flags)¶
當從 self 請求緩衝區時呼叫(例如,由
memoryview
建構函式呼叫)。 flags 引數是一個整數,表示請求的緩衝區的型別,例如,影響返回的緩衝區是隻讀還是可寫的。inspect.BufferFlags
提供了一種解釋標誌的便捷方法。該方法必須返回一個memoryview
物件。
- object.__release_buffer__(self, buffer)¶
當不再需要緩衝區時呼叫。 buffer 引數是一個
memoryview
物件,該物件先前由__buffer__()
返回。 該方法必須釋放與緩衝區關聯的任何資源。此方法應返回None
。不需要執行任何清理的緩衝區物件不需要實現此方法。
3.12 版本新增。
另請參閱
- PEP 688 - 使緩衝區協議在 Python 中可訪問
引入了 Python
__buffer__
和__release_buffer__
方法。collections.abc.Buffer
緩衝區型別的 ABC。
3.3.12. 特殊方法查詢¶
對於自定義類,只有在物件的型別上定義而不是在物件的例項字典中定義時,才能保證特殊方法的隱式呼叫正常工作。 這就是以下程式碼引發異常的原因
>>> class C:
... pass
...
>>> c = C()
>>> c.__len__ = lambda: 5
>>> len(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'C' has no len()
此行為背後的原理在於許多特殊方法,例如 __hash__()
和 __repr__()
,它們由所有物件(包括型別物件)實現。如果這些方法的隱式查詢使用常規查詢過程,則在型別物件本身上呼叫時它們將失敗
>>> 1 .__hash__() == hash(1)
True
>>> int.__hash__() == hash(int)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: descriptor '__hash__' of 'int' object needs an argument
以這種方式錯誤地嘗試呼叫類的未繫結方法有時被稱為“元類混淆”,並且透過在查詢特殊方法時繞過例項來避免
>>> type(1).__hash__(1) == hash(1)
True
>>> type(int).__hash__(int) == hash(int)
True
除了為了正確性而繞過任何例項屬性之外,隱式特殊方法查詢通常還會繞過物件的元類的 __getattribute__()
方法
>>> class Meta(type):
... def __getattribute__(*args):
... print("Metaclass getattribute invoked")
... return type.__getattribute__(*args)
...
>>> class C(object, metaclass=Meta):
... def __len__(self):
... return 10
... def __getattribute__(*args):
... print("Class getattribute invoked")
... return object.__getattribute__(*args)
...
>>> c = C()
>>> c.__len__() # Explicit lookup via instance
Class getattribute invoked
10
>>> type(c).__len__(c) # Explicit lookup via type
Metaclass getattribute invoked
10
>>> len(c) # Implicit lookup
10
以這種方式繞過 __getattribute__()
機制為直譯器內的速度最佳化提供了很大的空間,但代價是在處理特殊方法時有一定的靈活性(特殊方法必須在類物件本身上設定才能被直譯器一致地呼叫)。
3.4. 協程¶
3.4.1. 可等待物件¶
一個 可等待 物件通常實現一個 __await__()
方法。 從 async def
函式返回的協程物件是可等待的。
注意
從使用 types.coroutine()
修飾的生成器返回的 生成器迭代器 物件也是可等待的,但它們不實現 __await__()
。
- object.__await__(self)¶
必須返回一個 迭代器。 應使用它來實現 可等待 物件。 例如,
asyncio.Future
實現此方法以與await
表示式相容。object
類本身不是可等待的,並且不提供此方法。
在 3.5 版本中新增。
另請參閱
有關可等待物件的更多資訊,請參閱 PEP 492。
3.4.2. 協程物件¶
協程物件是 可等待 物件。可以透過呼叫 __await__()
並迭代結果來控制協程的執行。 當協程執行完成並返回時,迭代器會引發 StopIteration
異常,並且該異常的 value
屬性儲存返回值。 如果協程引發異常,則該異常將由迭代器傳播。 協程不應直接引發未處理的 StopIteration
異常。
協程也具有以下列出的方法,這些方法類似於生成器的方法(請參閱 生成器-迭代器方法)。但是,與生成器不同,協程不直接支援迭代。
在 3.5.2 版本中更改: 多次等待一個協程會引發 RuntimeError
錯誤。
- coroutine.send(value)¶
啟動或恢復協程的執行。如果 value 為
None
,則等效於推進由__await__()
返回的迭代器。如果 value 不為None
,則此方法會委託給導致協程掛起的迭代器的send()
方法。結果(返回值、StopIteration
或其他異常)與迭代__await__()
返回值時相同,如上所述。
- coroutine.throw(value)¶
- coroutine.throw(type[, value[, traceback]])
在協程中引發指定的異常。如果迭代器具有此方法,則此方法會委託給導致協程掛起的迭代器的
throw()
方法。否則,異常會在暫停點引發。結果(返回值、StopIteration
或其他異常)與迭代__await__()
返回值時相同,如上所述。如果協程中未捕獲該異常,它將傳播回撥用方。在 3.12 版本中更改: 第二個簽名 (type[, value[, traceback]]) 已棄用,可能會在 Python 的未來版本中刪除。
- coroutine.close()¶
導致協程清理自身並退出。如果協程被掛起,此方法首先會委託給導致協程掛起的迭代器的
close()
方法(如果它有此方法)。然後,它會在暫停點引發GeneratorExit
,導致協程立即清理自身。最後,協程會被標記為已完成執行,即使它從未啟動過。協程物件在即將被銷燬時會自動使用上述過程關閉。
3.4.3. 非同步迭代器¶
非同步迭代器可以在其 __anext__
方法中呼叫非同步程式碼。
非同步迭代器可以在 async for
語句中使用。
object
類本身不提供這些方法。
- object.__aiter__(self)¶
必須返回一個非同步迭代器物件。
- object.__anext__(self)¶
必須返回一個可等待物件,該物件會生成迭代器的下一個值。當迭代結束時,應引發
StopAsyncIteration
錯誤。
非同步可迭代物件的示例
class Reader:
async def readline(self):
...
def __aiter__(self):
return self
async def __anext__(self):
val = await self.readline()
if val == b'':
raise StopAsyncIteration
return val
在 3.5 版本中新增。
在 3.7 版本中更改: 在 Python 3.7 之前,__aiter__()
可以返回一個可等待物件,該物件會解析為 非同步迭代器。
從 Python 3.7 開始,__aiter__()
必須返回一個非同步迭代器物件。返回任何其他內容都會導致 TypeError
錯誤。
3.4.4. 非同步上下文管理器¶
非同步上下文管理器是一個上下文管理器,它能夠在 __aenter__
和 __aexit__
方法中暫停執行。
非同步上下文管理器可以在 async with
語句中使用。
object
類本身不提供這些方法。
- object.__aenter__(self)¶
在語義上類似於
__enter__()
,唯一的區別是它必須返回一個可等待物件。
- object.__aexit__(self, exc_type, exc_value, traceback)¶
在語義上類似於
__exit__()
,唯一的區別是它必須返回一個可等待物件。
非同步上下文管理器類的示例
class AsyncContextManager:
async def __aenter__(self):
await log('entering context')
async def __aexit__(self, exc_type, exc, tb):
await log('exiting context')
在 3.5 版本中新增。
腳註