3. 資料模型¶
3.1. 物件、值和型別¶
物件是 Python 對資料的抽象。Python 程式中的所有資料都由物件或物件之間的關係表示。(從某種意義上說,並符合馮·諾依曼的“儲存程式計算機”模型,程式碼也由物件表示。)
每個物件都有一個標識、一個型別和一個值。物件的 標識 一旦建立就不會改變;你可以將其視為物件在記憶體中的地址。is
運算子比較兩個物件的標識;id()
函式返回一個表示其標識的整數。
CPython 實現細節: 對於 CPython,id(x)
是儲存 x
的記憶體地址。
物件的型別決定了該物件支援的操作(例如,“它有長度嗎?”),並定義了該型別物件可能的值。type()
函式返回物件的型別(它本身也是一個物件)。與其標識一樣,物件的 型別 也是不可變的。[1]
某些物件的 值 可以改變。值可以改變的物件稱為 可變 的;一旦建立其值就不可改變的物件稱為 不可變 的。(包含對可變物件引用的不可變容器物件,當後者值改變時,其值也可能改變;但是容器仍被認為是不可變的,因為其包含的物件集合不能改變。因此,不可變性不嚴格等同於值不可變,它更為微妙。)物件的 mutability 由其型別決定;例如,數字、字串和元組是不可變的,而字典和列表是可變的。
物件從不被顯式銷燬;但是,當它們變得不可訪問時,它們可能會被垃圾回收。實現允許推遲垃圾回收或完全省略它——垃圾回收的實現方式是實現質量問題,只要沒有收集到仍然可訪問的物件即可。
CPython 實現細節: CPython 目前使用引用計數方案,並(可選地)延遲檢測迴圈連結的垃圾,這會在大多數物件變得不可訪問時立即收集它們,但不能保證收集包含迴圈引用的垃圾。有關控制迴圈垃圾收集的資訊,請參閱 gc
模組的文件。其他實現方式不同,CPython 也可能改變。不要依賴於物件變得不可訪問時立即終結(因此應始終顯式關閉檔案)。
請注意,使用實現的跟蹤或除錯工具可能會使通常可回收的物件保持活動狀態。另請注意,使用 try
…except
語句捕獲異常可能會使物件保持活動狀態。
一些物件包含對“外部”資源的引用,例如開啟的檔案或視窗。這些資源在物件被垃圾回收時會釋放,但由於不保證會發生垃圾回收,因此這些物件還提供了一種顯式釋放外部資源的方式,通常是 close()
方法。強烈建議程式顯式關閉此類物件。try
…finally
語句和 with
語句提供了方便的方式來完成此操作。
一些物件包含對其他物件的引用;這些物件被稱為 容器。容器的例子有元組、列表和字典。引用是容器值的一部分。在大多數情況下,當我們談論容器的值時,我們指的是包含物件的值,而不是它們的標識;然而,當我們談論容器的可變性時,僅涉及直接包含物件的標識。因此,如果一個不可變容器(如元組)包含對可變物件的引用,則當該可變物件改變時,其值也會改變。
型別影響物件行為的幾乎所有方面。甚至物件標識的重要性在某種程度上也受到影響:對於不可變型別,計算新值的操作實際上可能會返回對具有相同型別和值的任何現有物件的引用,而對於可變物件則不允許這樣做。例如,在 a = 1; b = 1
之後,a 和 b 可能引用也可能不引用值為一的同一物件,這取決於實現。這是因為 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
已被棄用。
3.14 版中已更改: 在布林上下文中求值 NotImplemented
現在會引發 TypeError
。之前它求值為 True
並在 Python 3.9 後發出 DeprecationWarning
。
3.2.3. Ellipsis¶
這種型別只有一個值。只有一個物件具有此值。此物件透過字面量 ...
或內建名稱 Ellipsis
訪問。其真值為 True。
3.2.4. numbers.Number
¶
這些是透過數字字面量建立的,並由算術運算子和算術內建函式作為結果返回。數字物件是不可變的;一旦建立,它們的值永不改變。Python 數字當然與數學數字密切相關,但受限於計算機中數字表示的限制。
由 __repr__()
和 __str__()
計算的數值類的字串表示具有以下屬性
它們是有效的數字字面量,當傳遞給其類建構函式時,會生成一個具有原始數字值的物件。
如果可能,表示為十進位制。
不顯示前導零,小數點前的一個零除外。
不顯示尾隨零,小數點後的一個零除外。
僅當數字為負數時才顯示符號。
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
),則集合中只能包含其中一個。
目前有兩種固有的集合型別
- 集合
- 凍結集合
這些表示一個不可變集合。它們由內建的
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.14 版本加入。 |
|
|
|
3.12 新版功能. |
函式物件還支援獲取和設定任意屬性,例如,可用於將元資料附加到函式。常規屬性點符號用於獲取和設定此類屬性。
CPython 實現細節: CPython 當前的實現只支援使用者定義函式上的函式屬性。將來可能會支援 內建函式 上的函式屬性。
3.2.8.2. 例項方法¶
一個例項方法物件結合了一個類、一個類例項和任何可呼叫物件(通常是使用者定義函式)。
特殊只讀屬性
|
引用方法 繫結 到的類例項物件 |
|
引用原始的 函式物件 |
|
方法的文件(與 |
|
方法的名稱(與 |
|
定義方法的模組的名稱,如果不可用則為 |
方法還支援訪問(但不設定)底層 函式物件 上的任意函式屬性。
如果類的一個屬性(可能透過該類的一個例項)是使用者定義的 函式物件 或 classmethod
物件,則在獲取該類的屬性時可能會建立使用者定義的方法物件。
當透過其某個例項從類中檢索使用者定義的 函式物件 來建立例項方法物件時,其 __self__
屬性是該例項,並且該方法物件被稱為 繫結 的。新方法的 __func__
屬性是原始函式物件。
當透過從類或例項中檢索 classmethod
物件來建立例項方法物件時,儲存在 __self__
中的“類例項”實際上將是類本身,因此呼叫 x.f(1)
或 C.f(1)
都等價於呼叫 f(C,1)
,其中 f
是類方法底層的函式。
當例項方法物件被呼叫時,底層函式 (__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__
的最佳實踐,請參閱annotationlib
。
- module.__annotate__¶
此模組的 註解函式,如果模組沒有註解則為
None
。另請參閱:__annotate__
屬性。在 3.14 版本加入。
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.14 版本加入。 |
|
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
中的位來指示程式碼物件是否以特定特性啟用編譯。請參閱 compiler_flag
。
co_flags
中的其他位保留供內部使用。
如果程式碼物件表示一個函式並具有文件字串,則 CO_HAS_DOCSTRING
位在 co_flags
中設定,並且 co_consts
中的第一個項是函式的文件字串。
3.2.13.1.2. 程式碼物件上的方法¶
- codeobject.co_positions()¶
返回一個迭代器,它迭代程式碼物件中每個 位元組碼 指令的原始碼位置。
迭代器返回包含
(start_line, end_line, start_column, end_column)
的元組
。第 *i* 個元組對應於編譯到第 *i* 個程式碼單元的原始碼位置。列資訊是給定源行上 0 索引的 utf-8 位元組偏移量。此位置資訊可能缺失。以下是一些可能發生這種情況的非詳盡列表:
發生這種情況時,元組的某些或所有元素可能為
None
。在 3.11 版本中新增。
備註
此功能要求將列位置儲存在程式碼物件中,這可能會導致編譯後的 Python 檔案或直譯器記憶體使用量略有增加。為了避免儲存額外資訊和/或停用列印額外回溯資訊,可以使用
-X
no_debug_ranges
命令列標誌或PYTHONNODEBUGRANGES
環境變數。
- codeobject.co_lines()¶
返回一個迭代器,它生成有關連續 位元組碼 範圍的資訊。生成的每個項都是一個
(start, end, lineno)
元組
生成的項將具有以下屬性
生成的第一個範圍的
start
為 0。(start, end)
範圍將是非遞減和連續的。也就是說,對於任何一對元組
,第二個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.14 版本加入。 |
3.2.13.2.2. 特殊的、可寫的屬性¶
|
如果不是 |
|
將此屬性設定為 |
|
將此屬性設定為 |
|
幀的當前行號——從跟蹤函式中寫入此行號會跳轉到給定行(僅適用於最底層的幀)。偵錯程式可以透過寫入此屬性來實現 Jump 命令(又稱 Set Next Statement)。 |
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__()
方法將像__init__(self[, ...])
一樣被呼叫,其中 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
的引用計數減一,後者僅在x
的引用計數達到零時才呼叫。CPython 實現細節: 引用迴圈可能會阻止物件的引用計數降至零。在這種情況下,迴圈將稍後由迴圈垃圾收集器檢測並刪除。引用迴圈的常見原因是異常被捕獲到區域性變數中。然後幀的區域性變數引用異常,異常引用其自身的追溯,追溯引用追溯中捕獲的所有幀的區域性變數。
參見
有關
gc
模組的文件。警告
由於呼叫
__del__()
方法的危險情況,其執行期間發生的異常將被忽略,而是向sys.stderr
列印警告。特別是
- object.__repr__(self)¶
由內建函式
repr()
呼叫,用於計算物件的“正式”字串表示形式。如果可能,這應該看起來像一個有效的 Python 表示式,可用於(在適當的環境中)重新建立具有相同值的物件。如果不可能,則應返回<...some useful description...>
形式的字串。返回值必須是字串物件。如果一個類定義了__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
。一個定義了自己明確引發TypeError
的__hash__()
的類,在isinstance(obj, collections.abc.Hashable)
呼叫中會被錯誤地識別為可雜湊。備註
預設情況下,str 和 bytes 物件的
__hash__()
值會用一個不可預測的隨機值進行“加鹽”。儘管它們在單個 Python 程序中保持不變,但在重複呼叫 Python 之間是不可預測的。這旨在提供針對因精心選擇的輸入而導致的拒絕服務攻擊的保護,這些輸入利用了字典插入的最壞情況效能,即 *O*(*n*2) 複雜度。有關詳細資訊,請參閱http://ocert.org/advisories/ocert-2011-003.html。
雜湊值變化會影響集合的迭代順序。Python 從未對此順序做出保證(並且它通常在32位和64位構建之間有所不同)。
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)
。對於某些敏感屬性訪問,會引發審計事件
object.__getattr__
,引數為obj
和name
。
- object.__setattr__(self, name, value)¶
嘗試屬性賦值時呼叫。這將代替正常機制(即在例項字典中儲存值)呼叫。name 是屬性名稱,value 是要分配給它的值。
如果
__setattr__()
想要賦值給例項屬性,它應該呼叫基類中同名的方法,例如object.__setattr__(self, name, value)
。對於某些敏感屬性賦值,會引發審計事件
object.__setattr__
,引數為obj
、name
、value
。
- object.__delattr__(self, name)¶
類似於
__setattr__()
,但用於屬性刪除而不是賦值。僅當del obj.name
對物件有意義時才應實現此方法。對於某些敏感屬性刪除,會引發審計事件
object.__delattr__
,引數為obj
和name
。
3.3.2.1. 自定義模組屬性訪問¶
特殊名稱__getattr__
和__dir__
也可以用於自定義模組屬性的訪問。模組級別的__getattr__
函式應該接受一個引數,即屬性的名稱,並返回計算值或引發AttributeError
。如果透過正常查詢(即object.__getattribute__()
)在模組物件上找不到屬性,那麼在引發AttributeError
之前,會在模組__dict__
中搜索__getattr__
。如果找到,則使用屬性名稱呼叫它並返回結果。
__dir__
函式不應接受任何引數,並返回一個表示模組上可訪問名稱的字串可迭代物件。如果存在,此函式將覆蓋模組上的標準dir()
搜尋。
- module.__class__¶
為了更細粒度地自定義模組行為(設定屬性、特性等),可以將模組物件的__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 - Module __getattr__ and __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 繫結
像
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__。
如果使用
dictionary
來分配 __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 - 對 typing 模組和泛型型別的核心支援
引入
__class_getitem__()
,並概述了何時 下標 會導致呼叫__class_getitem__()
而不是__getitem__()
。
3.3.6. 模擬可呼叫物件¶
3.3.7. 模擬容器型別¶
可以定義以下方法來實現容器物件。它們都不是由 object
類本身提供的。容器通常是 序列(例如 lists
或 tuples
)或 對映(例如 dictionaries),但也可能表示其他容器。第一組方法用於模擬序列或模擬對映;區別在於,對於序列,允許的鍵應該是整數 k,其中 0 <= k < N
,其中 N 是序列的長度,或者是 slice
物件,它定義了一個項的範圍。還建議對映提供 keys()
、values()
、items()
、get()
、clear()
、setdefault()
、pop()
、popitem()
、copy()
和 update()
方法,其行為類似於 Python 標準 dictionary
物件的方法。collections.abc
模組提供了一個 MutableMapping
抽象基類,以幫助從 __getitem__()
、__setitem__()
、__delitem__()
和 keys()
的基本集合建立這些方法。
可變序列應提供方法 append()
、clear()
、count()
、extend()
、index()
、insert()
、pop()
、remove()
和 reverse()
,類似於 Python 標準 list
物件。最後,序列型別應透過定義下述方法 __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 進行下標時,可能會呼叫特殊的類方法
__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__()
呼叫,以實現字典子類的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)
是type(x)
的子類,則會呼叫type(y).__rsub__(y, x)
。[5]請注意,如果內建
pow()
函式的三引數版本要得到支援,則__rpow__()
應該定義為接受可選的第三個引數。3.14 版本中的改動: 三引數的
pow()
現在會在必要時嘗試呼叫__rpow__()
。之前它僅在雙引數pow()
和二進位制冪運算子中呼叫。備註
如果右運算元的型別是左運算元型別的子類,並且該子類為該操作提供了不同的反射方法實現,則此方法將在左運算元的非反射方法之前呼叫。此行為允許子類覆蓋其祖先的操作。
- 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.__add__(y)
和y.__radd__(x)
,就像計算x + y
時一樣。在某些情況下,增強型賦值可能會導致意外錯誤(請參閱 為什麼當加法有效時,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__()
。
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 引數是一個之前由
__buffer__()
返回的memoryview
物件。此方法必須釋放與緩衝區相關的任何資源。此方法應返回None
。不需要執行任何清理的緩衝區物件無需實現此方法。
3.12 新版功能.
參見
- PEP 688 - 使緩衝區協議在 Python 中可訪問
引入了 Python
__buffer__
和__release_buffer__
方法。collections.abc.Buffer
緩衝區型別的抽象基類。
3.3.12. 註解¶
函式、類和模組可能包含 註解,這是一種將資訊(通常是 型別提示)與符號關聯起來的方法。
- object.__annotations__¶
此屬性包含物件的註解。它是 惰性求值 的,因此訪問此屬性可能會執行任意程式碼並引發異常。如果求值成功,則此屬性將設定為一個字典,其中包含從變數名到註解的對映。
3.14 版本中的改動: 註解現在是惰性求值的。
- object.__annotate__(format)¶
一個 註解函式。返回一個新字典物件,將屬性/引數名對映到其註解值。
接受一個 format 引數,指定註解值的提供格式。它必須是
annotationlib.Format
列舉的成員,或者是一個與列舉成員對應的值的整數。如果註解函式不支援請求的格式,它必須引發
NotImplementedError
。註解函式必須始終支援VALUE
格式;當使用此格式呼叫時,它們不得引發NotImplementedError()
。當使用
VALUE
格式呼叫時,註解函式可能會引發NameError
;當請求任何其他格式時,它不得引發NameError
。如果物件沒有任何註解,
__annotate__
應該優先設定為None
(它不能被刪除),而不是設定為返回空字典的函式。在 3.14 版本加入。
參見
- PEP 649 — 使用描述符延遲求值註解
引入了註解的惰性求值和
__annotate__
函式。
3.3.13. 特殊方法查詢¶
對於自定義類,特殊方法的隱式呼叫只有在物件型別而非物件例項字典中定義時才能保證正確工作。這種行為就是以下程式碼引發異常的原因:
>>> 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 版本加入。
腳註