3. 數(shù)據(jù)模型?

3.1. 對(duì)象、值與類型?

對(duì)象 是 Python 中對(duì)數(shù)據(jù)的抽象。 Python 程序中的所有數(shù)據(jù)都是由對(duì)象或?qū)ο箝g關(guān)系來表示的。 (從某種意義上說,按照馮·諾依曼的“存儲(chǔ)程序計(jì)算機(jī)”模型,代碼本身也是由對(duì)象來表示的。)

每個(gè)對(duì)象都有各自的編號(hào)、類型和值。一個(gè)對(duì)象被創(chuàng)建后,它的 編號(hào) 就絕不會(huì)改變;你可以將其理解為該對(duì)象在內(nèi)存中的地址。 'is' 運(yùn)算符可以比較兩個(gè)對(duì)象的編號(hào)是否相同;id() 函數(shù)能返回一個(gè)代表其編號(hào)的整型數(shù)。

CPython implementation detail: 在 CPython 中,id(x) 就是存放 x 的內(nèi)存的地址。

對(duì)象的類型決定該對(duì)象所支持的操作 (例如 "對(duì)象是否有長度屬性?") 并且定義了該類型的對(duì)象可能的取值。type() 函數(shù)能返回一個(gè)對(duì)象的類型 (類型本身也是對(duì)象)。與編號(hào)一樣,一個(gè)對(duì)象的 類型 也是不可改變的。1

有些對(duì)象的 可以改變。值可以改變的對(duì)象被稱為 可變的;值不可以改變的對(duì)象就被稱為 不可變的。(一個(gè)不可變?nèi)萜鲗?duì)象如果包含對(duì)可變對(duì)象的引用,當(dāng)后者的值改變時(shí),前者的值也會(huì)改變;但是該容器仍屬于不可變對(duì)象,因?yàn)樗膶?duì)象集是不會(huì)改變的。因此,不可變并不嚴(yán)格等同于值不能改變,實(shí)際含義要更微妙。) 一個(gè)對(duì)象的可變性是由其類型決定的;例如,數(shù)字、字符串和元組是不可變的,而字典和列表是可變的。

對(duì)象絕不會(huì)被顯式地銷毀;然而,當(dāng)無法訪問時(shí)它們可能會(huì)被作為垃圾回收。允許具體的實(shí)現(xiàn)推遲垃圾回收或完全省略此機(jī)制 --- 如何實(shí)現(xiàn)垃圾回收是實(shí)現(xiàn)的質(zhì)量問題,只要可訪問的對(duì)象不會(huì)被回收即可。

CPython implementation detail: CPython 目前使用帶有 (可選) 延遲檢測循環(huán)鏈接垃圾的引用計(jì)數(shù)方案,會(huì)在對(duì)象不可訪問時(shí)立即回收其中的大部分,但不保證回收包含循環(huán)引用的垃圾。請(qǐng)查看 gc 模塊的文檔了解如何控制循環(huán)垃圾的收集相關(guān)信息。其他實(shí)現(xiàn)會(huì)有不同的行為方式,CPython 現(xiàn)有方式也可能改變。不要依賴不可訪問對(duì)象的立即終結(jié)機(jī)制 (所以你應(yīng)當(dāng)總是顯式地關(guān)閉文件)。

注意:使用實(shí)現(xiàn)的跟蹤或調(diào)試功能可能令正常情況下會(huì)被回收的對(duì)象繼續(xù)存活。還要注意通過 'try...except' 語句捕捉異常也可能令對(duì)象保持存活。

有些對(duì)象包含對(duì) "外部" 資源的引用,例如打開文件或窗口。當(dāng)對(duì)象被作為垃圾回收時(shí)這些資源也應(yīng)該會(huì)被釋放,但由于垃圾回收并不確保發(fā)生,這些對(duì)象還提供了明確地釋放外部資源的操作,通常為一個(gè) close() 方法。強(qiáng)烈推薦在程序中顯式關(guān)閉此類對(duì)象。'try...finally' 語句和 'with' 語句提供了進(jìn)行此種操作的更便捷方式。

有些對(duì)象包含對(duì)其他對(duì)象的引用;它們被稱為 容器。容器的例子有元組、列表和字典等。這些引用是容器對(duì)象值的組成部分。在多數(shù)情況下,當(dāng)談?wù)撘粋€(gè)容器的值時(shí),我們是指所包含對(duì)象的值而不是其編號(hào);但是,當(dāng)我們談?wù)撘粋€(gè)容器的可變性時(shí),則僅指其直接包含的對(duì)象的編號(hào)。因此,如果一個(gè)不可變?nèi)萜?(例如元組) 包含對(duì)一個(gè)可變對(duì)象的引用,則當(dāng)該可變對(duì)象被改變時(shí)容器的值也會(huì)改變。

類型會(huì)影響對(duì)象行為的幾乎所有方面。甚至對(duì)象編號(hào)的重要性也在某種程度上受到影響: 對(duì)于不可變類型,會(huì)得出新值的運(yùn)算實(shí)際上會(huì)返回對(duì)相同類型和取值的任一現(xiàn)有對(duì)象的引用,而對(duì)于可變類型來說這是不允許的。例如在 a = 1; b = 1 之后,ab 可能會(huì)也可能不會(huì)指向同一個(gè)值為一的對(duì)象,這取決于具體實(shí)現(xiàn),但是在 c = []; d = [] 之后,cd 保證會(huì)指向兩個(gè)不同、單獨(dú)的新建空列表。(請(qǐng)注意 c = d = [] 則是將同一個(gè)對(duì)象賦值給 cd。)

3.2. 標(biāo)準(zhǔn)類型層級(jí)結(jié)構(gòu)?

以下是 Python 內(nèi)置類型的列表。擴(kuò)展模塊 (具體實(shí)現(xiàn)會(huì)以 C, Java 或其他語言編寫) 可以定義更多的類型。未來版本的 Python 可能會(huì)加入更多的類型 (例如有理數(shù)、高效存儲(chǔ)的整型數(shù)組等等),不過新增類型往往都是通過標(biāo)準(zhǔn)庫來提供的。

以下部分類型的描述中包含有 '特殊屬性列表' 段落。這些屬性提供對(duì)具體實(shí)現(xiàn)的訪問而非通常使用。它們的定義在未來可能會(huì)改變。

None

此類型只有一種取值。是一個(gè)具有此值的單獨(dú)對(duì)象。此對(duì)象通過內(nèi)置名稱 None 訪問。在許多情況下它被用來表示空值,例如未顯式指明返回值的函數(shù)將返回 None。它的邏輯值為假。

NotImplemented

此類型只有一種取值。是一個(gè)具有此值的單獨(dú)對(duì)象。此對(duì)象通過內(nèi)置名稱 NotImplemented 訪問。數(shù)值方法和豐富比較方法如未實(shí)現(xiàn)指定運(yùn)算符表示的運(yùn)算則應(yīng)返回此值。(解釋器會(huì)根據(jù)指定運(yùn)算符繼續(xù)嘗試反向運(yùn)算或其他回退操作)。它的邏輯值為真。

詳情參見 實(shí)現(xiàn)算術(shù)運(yùn)算

Ellipsis

此類型只有一種取值。是一個(gè)具有此值的單獨(dú)對(duì)象。此對(duì)象通過字面值 ... 或內(nèi)置名稱 Ellipsis 訪問。它的邏輯值為真。

numbers.Number

此類對(duì)象由數(shù)字字面值創(chuàng)建,并會(huì)被作為算術(shù)運(yùn)算符和算術(shù)內(nèi)置函數(shù)的返回結(jié)果。數(shù)字對(duì)象是不可變的;一旦創(chuàng)建其值就不再改變。Python 中的數(shù)字當(dāng)然非常類似數(shù)學(xué)中的數(shù)字,但也受限于計(jì)算機(jī)中的數(shù)字表示方法。

Python 區(qū)分整型數(shù)、浮點(diǎn)型數(shù)和復(fù)數(shù):

numbers.Integral

此類對(duì)象表示數(shù)學(xué)中整數(shù)集合的成員 (包括正數(shù)和負(fù)數(shù))。

整型數(shù)可細(xì)分為兩種類型:

整型 (int)

此類對(duì)象表示任意大小的數(shù)字,僅受限于可用的內(nèi)存 (包括虛擬內(nèi)存)。在變換和掩碼運(yùn)算中會(huì)以二進(jìn)制表示,負(fù)數(shù)會(huì)以 2 的補(bǔ)碼表示,看起來像是符號(hào)位向左延伸補(bǔ)滿空位。

布爾型 (bool)

此類對(duì)象表示邏輯值 False 和 True。代表 FalseTrue 值的兩個(gè)對(duì)象是唯二的布爾對(duì)象。布爾類型是整型的子類型,兩個(gè)布爾值在各種場合的行為分別類似于數(shù)值 0 和 1,例外情況只有在轉(zhuǎn)換為字符串時(shí)分別返回字符串 "False""True"

整型數(shù)表示規(guī)則的目的是在涉及負(fù)整型數(shù)的變換和掩碼運(yùn)算時(shí)提供最為合理的解釋。

numbers.Real (float)

此類對(duì)象表示機(jī)器級(jí)的雙精度浮點(diǎn)數(shù)。其所接受的取值范圍和溢出處理將受制于底層的機(jī)器架構(gòu) (以及 C 或 Java 實(shí)現(xiàn))。Python 不支持單精度浮點(diǎn)數(shù);支持后者通常的理由是節(jié)省處理器和內(nèi)存消耗,但這點(diǎn)節(jié)省相對(duì)于在 Python 中使用對(duì)象的開銷來說太過微不足道,因此沒有理由包含兩種浮點(diǎn)數(shù)而令該語言變得復(fù)雜。

numbers.Complex (complex)

此類對(duì)象以一對(duì)機(jī)器級(jí)的雙精度浮點(diǎn)數(shù)來表示復(fù)數(shù)值。有關(guān)浮點(diǎn)數(shù)的附帶規(guī)則對(duì)其同樣有效。一個(gè)復(fù)數(shù)值 z 的實(shí)部和虛部可通過只讀屬性 z.realz.imag 來獲取。

序列

此類對(duì)象表示以非負(fù)整數(shù)作為索引的有限有序集。內(nèi)置函數(shù) len() 可返回一個(gè)序列的條目數(shù)量。當(dāng)一個(gè)序列的長度為 n 時(shí),索引集包含數(shù)字 0, 1, ..., n-1。序列 a 的條目 i 可通過 a[i] 選擇。

序列還支持切片: a[i:j] 選擇索引號(hào)為 k 的所有條目,i <= k < j。當(dāng)用作表達(dá)式時(shí),序列的切片就是一個(gè)與序列類型相同的新序列。新序列的索引還是從 0 開始。

有些序列還支持帶有第三個(gè) "step" 形參的 "擴(kuò)展切片": a[i:j:k] 選擇 a 中索引號(hào)為 x 的所有條目,x = i + n*k, n >= 0i <= x < j

序列可根據(jù)其可變性來加以區(qū)分:

不可變序列

不可變序列類型的對(duì)象一旦創(chuàng)建就不能再改變。(如果對(duì)象包含對(duì)其他對(duì)象的引用,其中的可變對(duì)象就是可以改變的;但是,一個(gè)不可變對(duì)象所直接引用的對(duì)象集是不能改變的。)

以下類型屬于不可變對(duì)象:

字符串

字符串是由 Unicode 碼位值組成的序列。范圍在 U+0000 - U+10FFFF 之內(nèi)的所有碼位值都可在字符串中使用。Python 沒有 char 類型;而是將字符串中的每個(gè)碼位表示為一個(gè)長度為 1 的字符串對(duì)象。內(nèi)置函數(shù) ord() 可將一個(gè)碼位由字符串形式轉(zhuǎn)換成一個(gè)范圍在 0 - 10FFFF 之內(nèi)的整型數(shù);chr() 可將一個(gè)范圍在 0 - 10FFFF 之內(nèi)的整型數(shù)轉(zhuǎn)換為長度為 1 的對(duì)應(yīng)字符串對(duì)象。str.encode() 可以使用指定的文本編碼將 str 轉(zhuǎn)換為 bytes,而 bytes.decode() 則可以實(shí)現(xiàn)反向的解碼。

元組

一個(gè)元組中的條目可以是任意 Python 對(duì)象。包含兩個(gè)或以上條目的元組由逗號(hào)分隔的表達(dá)式構(gòu)成。只有一個(gè)條目的元組 ('單項(xiàng)元組') 可通過在表達(dá)式后加一個(gè)逗號(hào)來構(gòu)成 (一個(gè)表達(dá)式本身不能創(chuàng)建為元組,因?yàn)閳A括號(hào)要用來設(shè)置表達(dá)式分組)。一個(gè)空元組可通過一對(duì)內(nèi)容為空的圓括號(hào)創(chuàng)建。

字節(jié)串

字節(jié)串對(duì)象是不可變的數(shù)組。其中每個(gè)條目都是一個(gè) 8 位字節(jié),以取值范圍 0 <= x < 256 的整型數(shù)表示。字節(jié)串字面值 (例如 b'abc') 和內(nèi)置的 bytes() 構(gòu)造器可被用來創(chuàng)建字節(jié)串對(duì)象。字節(jié)串對(duì)象還可以通過 decode() 方法解碼為字符串。

可變序列

可變序列在被創(chuàng)建后仍可被改變。下標(biāo)和切片標(biāo)注可被用作賦值和 del (刪除) 語句的目標(biāo)。

目前有兩種內(nèi)生可變序列類型:

列表

列表中的條目可以是任意 Python 對(duì)象。列表由用方括號(hào)括起并由逗號(hào)分隔的多個(gè)表達(dá)式構(gòu)成。(注意創(chuàng)建長度為 0 或 1 的列表無需使用特殊規(guī)則。)

字節(jié)數(shù)組

字節(jié)數(shù)組對(duì)象屬于可變數(shù)組??梢酝ㄟ^內(nèi)置的 bytearray() 構(gòu)造器來創(chuàng)建。除了是可變的 (因而也是不可哈希的),在其他方面字節(jié)數(shù)組提供的接口和功能都與不可變的 bytes 對(duì)象一致。

擴(kuò)展模塊 array 提供了一個(gè)額外的可變序列類型示例,collections 模塊也是如此。

集合類型

此類對(duì)象表示由不重復(fù)且不可變對(duì)象組成的無序且有限的集合。因此它們不能通過下標(biāo)來索引。但是它們可被迭代,也可用內(nèi)置函數(shù) len() 返回集合中的條目數(shù)。集合常見的用處是快速成員檢測,去除序列中的重復(fù)項(xiàng),以及進(jìn)行交、并、差和對(duì)稱差等數(shù)學(xué)運(yùn)算。

對(duì)于集合元素所采用的不可變規(guī)則與字典的鍵相同。注意數(shù)字類型遵循正常的數(shù)字比較規(guī)則: 如果兩個(gè)數(shù)字相等 (例如 11.0),則同一集合中只能包含其中一個(gè)。

目前有兩種內(nèi)生集合類型:

集合

此類對(duì)象表示可變集合。它們可通過內(nèi)置的 set() 構(gòu)造器創(chuàng)建,并且創(chuàng)建之后可以通過方法進(jìn)行修改,例如 add()

凍結(jié)集合

此類對(duì)象表示不可變集合。它們可通過內(nèi)置的 frozenset() 構(gòu)造器創(chuàng)建。由于 frozenset 對(duì)象不可變且 hashable,它可以被用作另一個(gè)集合的元素或是字典的鍵。

映射

此類對(duì)象表示由任意索引集合所索引的對(duì)象的集合。通過下標(biāo) a[k] 可在映射 a 中選擇索引為 k 的條目;這可以在表達(dá)式中使用,也可作為賦值或 del 語句的目標(biāo)。內(nèi)置函數(shù) len() 可返回一個(gè)映射中的條目數(shù)。

目前只有一種內(nèi)生映射類型:

字典

此類對(duì)象表示由幾乎任意值作為索引的有限個(gè)對(duì)象的集合。不可作為鍵的值類型只有包含列表或字典或其他可變類型,通過值而非對(duì)象編號(hào)進(jìn)行比較的值,其原因在于高效的字典實(shí)現(xiàn)需要使用鍵的哈希值以保持一致性。用作鍵的數(shù)字類型遵循正常的數(shù)字比較規(guī)則: 如果兩個(gè)數(shù)字相等 (例如 11.0) 則它們均可來用來索引同一個(gè)字典條目。

字典會(huì)保留插入順序,這意味著鍵將以它們被添加的順序在字典中依次產(chǎn)生。 替換某個(gè)現(xiàn)有的鍵不會(huì)改變其順序,但是移除某個(gè)鍵再重新插入則會(huì)將其添加到末尾而不會(huì)保留其原有位置。

字典是可變的;它們可通過 {...} 標(biāo)注來創(chuàng)建 (參見 字典顯示 小節(jié))。

擴(kuò)展模塊 dbm.ndbmdbm.gnu 提供了額外的映射類型示例,collections 模塊也是如此。

在 3.7 版更改: 在 Python 3.6 版之前字典不會(huì)保留插入順序。 在 CPython 3.6 中插入順序會(huì)被保留,但這在當(dāng)時(shí)被當(dāng)作是一個(gè)實(shí)現(xiàn)細(xì)節(jié)而非確定的語言特性。

可調(diào)用類型

此類型可以被應(yīng)用于函數(shù)調(diào)用操作 (參見 調(diào)用 小節(jié)):

用戶定義函數(shù)

用戶定義函數(shù)對(duì)象可通過函數(shù)定義來創(chuàng)建 (參見 函數(shù)定義 小節(jié))。它被調(diào)用時(shí)應(yīng)附帶一個(gè)參數(shù)列表,其中包含的條目應(yīng)與函數(shù)所定義的形參列表一致。

特殊屬性:

屬性

含義

__doc__

該函數(shù)的文檔字符串,沒有則為 None;不會(huì)被子類繼承。

可寫

__name__

該函數(shù)的名稱。

可寫

__qualname__

該函數(shù)的 qualified name

3.3 新版功能.

可寫

__module__

該函數(shù)所屬模塊的名稱,沒有則為 None。

可寫

__defaults__

由具有默認(rèn)值的參數(shù)的默認(rèn)參數(shù)值組成的元組,如無任何參數(shù)具有默認(rèn)值則為 None。

可寫

__code__

表示編譯后的函數(shù)體的代碼對(duì)象。

可寫

__globals__

對(duì)存放該函數(shù)中全局變量的字典的引用 --- 函數(shù)所屬模塊的全局命名空間。

只讀

__dict__

命名空間支持的函數(shù)屬性。

可寫

__closure__

None 或包含該函數(shù)可用變量的綁定的單元的元組。有關(guān) cell_contents 屬性的詳情見下。

只讀

__annotations__

包含參數(shù)標(biāo)注的字典。字典的鍵是參數(shù)名,如存在返回標(biāo)注則為 'return'

可寫

__kwdefaults__

僅包含關(guān)鍵字參數(shù)默認(rèn)值的字典。

可寫

大部分標(biāo)有 "Writable" 的屬性均會(huì)檢查賦值的類型。

函數(shù)對(duì)象也支持獲取和設(shè)置任意屬性,例如這可以被用來給函數(shù)附加元數(shù)據(jù)。使用正規(guī)的屬性點(diǎn)號(hào)標(biāo)注獲取和設(shè)置此類屬性。注意當(dāng)前實(shí)現(xiàn)僅支持用戶定義函數(shù)屬性。未來可能會(huì)增加支持內(nèi)置函數(shù)屬性。

單元對(duì)象具有 cell_contents 屬性。這可被用來獲取以及設(shè)置單元的值。

有關(guān)函數(shù)定義的額外信息可以從其代碼對(duì)象中提??;參見下文對(duì)內(nèi)部類型的描述。

實(shí)例方法

實(shí)例方法用于結(jié)合類、類實(shí)例和任何可調(diào)用對(duì)象 (通常為用戶定義函數(shù))。

特殊的只讀屬性: __self__ 為類實(shí)例對(duì)象本身,__func__ 為函數(shù)對(duì)象;__doc__ 為方法的文檔 (與 __func__.__doc__ 作用相同);__name__ 為方法名稱 (與 __func__.__name__ 作用相同);__module__ 為方法所屬模塊的名稱,沒有則為 None。

方法還支持獲取 (但不能設(shè)置) 下層函數(shù)對(duì)象的任意函數(shù)屬性。

用戶定義方法對(duì)象可在獲取一個(gè)類的屬性時(shí)被創(chuàng)建 (也可能通過該類的一個(gè)實(shí)例),如果該屬性為用戶定義函數(shù)對(duì)象或類方法對(duì)象。

當(dāng)通過從類實(shí)例獲取一個(gè)用戶定義函數(shù)對(duì)象的方式創(chuàng)建一個(gè)實(shí)例方法對(duì)象時(shí),類實(shí)例對(duì)象的 __self__ 屬性即為該實(shí)例,并會(huì)綁定方法對(duì)象。該新建方法的 __func__ 屬性就是原來的函數(shù)對(duì)象。

當(dāng)通過從類或?qū)嵗@取另一個(gè)方法對(duì)象的方式創(chuàng)建一個(gè)用戶定義方法對(duì)象時(shí),其行為將等同于一個(gè)函數(shù)對(duì)象,例外的只有新實(shí)例的 __func__ 屬性將不是原來的方法對(duì)象,而是其 __func__ 屬性。

當(dāng)通過從類或?qū)嵗@取一個(gè)類方法對(duì)象的方式創(chuàng)建一個(gè)實(shí)例對(duì)象時(shí),實(shí)例對(duì)象的 __self__ 屬性為該類本身,其 __func__ 屬性為類方法對(duì)應(yīng)的下層函數(shù)對(duì)象。

當(dāng)一個(gè)實(shí)例方法對(duì)象被調(diào)用時(shí),會(huì)調(diào)用對(duì)應(yīng)的下層函數(shù) (__func__),并將類實(shí)例 (__self__) 插入?yún)?shù)列表的開頭。例如,當(dāng) C 是一個(gè)包含了 f() 函數(shù)定義的類,而 xC 的一個(gè)實(shí)例,則調(diào)用 x.f(1) 就等同于調(diào)用 C.f(x, 1)

當(dāng)一個(gè)實(shí)例方法對(duì)象是衍生自一個(gè)類方法對(duì)象時(shí),保存在 __self__ 中的 "類實(shí)例" 實(shí)際上會(huì)是該類本身,因此無論是調(diào)用 x.f(1) 還是 C.f(1) 都等同于調(diào)用 f(C,1),其中 f 為對(duì)應(yīng)的下層函數(shù)。

請(qǐng)注意從函數(shù)對(duì)象到實(shí)例方法對(duì)象的變換會(huì)在每一次從實(shí)例獲取屬性時(shí)發(fā)生。在某些情況下,一種高效的優(yōu)化方式是將屬性賦值給一個(gè)本地變量并調(diào)用該本地變量。還要注意這樣的變換只發(fā)生于用戶定義函數(shù);其他可調(diào)用對(duì)象 (以及所有不可調(diào)用對(duì)象) 在被獲取時(shí)都不會(huì)發(fā)生變換。還有一個(gè)需要關(guān)注的要點(diǎn)是作為一個(gè)類實(shí)例屬性的用戶定義函數(shù)不會(huì)被轉(zhuǎn)換為綁定方法;這樣的變換 僅當(dāng) 函數(shù)是類屬性時(shí)才會(huì)發(fā)生。

生成器函數(shù)

一個(gè)使用 yield 語句 (見 yield 語句 章節(jié))的函數(shù)或方法被稱作一個(gè) 生成器函數(shù)。 這樣的函數(shù)在被調(diào)用時(shí),總是返回一個(gè)可以執(zhí)行函數(shù)體的迭代器對(duì)象:調(diào)用該迭代器的 iterator.__next__() 方法將會(huì)導(dǎo)致這個(gè)函數(shù)一直運(yùn)行直到它使用 yield 語句提供了一個(gè)值為止。 當(dāng)這個(gè)函數(shù)執(zhí)行 return 語句或者執(zhí)行到末尾時(shí),將引發(fā) StopIteration 異常并且這個(gè)迭代器將到達(dá)所返回的值集合的末尾。

協(xié)程函數(shù)

使用 async def 來定義的函數(shù)或方法就被稱為 協(xié)程函數(shù)。這樣的函數(shù)在被調(diào)用時(shí)會(huì)返回一個(gè) coroutine 對(duì)象。它可能包含 await 表達(dá)式以及 async withasync for 語句。詳情可參見 協(xié)程對(duì)象 一節(jié)。

異步生成器函數(shù)

使用 async def 來定義并包含 yield 語句的函數(shù)或方法就被稱為 異步生成器函數(shù)。這樣的函數(shù)在被調(diào)用時(shí)會(huì)返回一個(gè)異步迭代器對(duì)象,該對(duì)象可在 async for 語句中用來執(zhí)行函數(shù)體。

調(diào)用異步迭代器的 aiterator.__anext__() 方法將會(huì)返回一個(gè) awaitable,此對(duì)象會(huì)在被等待時(shí)執(zhí)行直到使用 yield 表達(dá)式輸出一個(gè)值。當(dāng)函數(shù)執(zhí)行時(shí)到空的 return 語句或是最后一條語句時(shí),將會(huì)引發(fā) StopAsyncIteration 異常,異步迭代器也會(huì)到達(dá)要輸出的值集合的末尾。

內(nèi)置函數(shù)

內(nèi)置函數(shù)對(duì)象是對(duì)于 C 函數(shù)的外部封裝。內(nèi)置函數(shù)的例子包括 len()math.sin() (math 是一個(gè)標(biāo)準(zhǔn)內(nèi)置模塊)。內(nèi)置函數(shù)參數(shù)的數(shù)量和類型由 C 函數(shù)決定。特殊的只讀屬性: __doc__ 是函數(shù)的文檔字符串,如果沒有則為 None; __name__ 是函數(shù)的名稱; __self__ 設(shè)定為 None (參見下一條目); __module__ 是函數(shù)所屬模塊的名稱,如果沒有則為 None。

內(nèi)置方法

此類型實(shí)際上是內(nèi)置函數(shù)的另一種形式,只不過還包含了一個(gè)傳入 C 函數(shù)的對(duì)象作為隱式的額外參數(shù)。內(nèi)置方法的一個(gè)例子是 alist.append(),其中 alist 為一個(gè)列表對(duì)象。在此示例中,特殊的只讀屬性 __self__ 會(huì)被設(shè)為 alist 所標(biāo)記的對(duì)象。

類是可調(diào)用的。此種對(duì)象通常是作為“工廠”來創(chuàng)建自身的實(shí)例,類也可以有重載 __new__() 的變體類型。調(diào)用的參數(shù)會(huì)傳給 __new__(),而且通常也會(huì)傳給 __init__() 來初始化新的實(shí)例。

類實(shí)例

任意類的實(shí)例通過在所屬類中定義 __call__() 方法即能成為可調(diào)用的對(duì)象。

模塊

模塊是 Python 代碼的基本組織單元,由 導(dǎo)入系統(tǒng) 創(chuàng)建,由 import 語句發(fā)起調(diào)用,或者通過 importlib.import_module() 和內(nèi)置的 __import__() 等函數(shù)發(fā)起調(diào)用。 模塊對(duì)象具有由字典對(duì)象實(shí)現(xiàn)的命名空間(這是被模塊中定義的函數(shù)的 __globals__ 屬性引用的字典)。 屬性引用被轉(zhuǎn)換為該字典中的查找,例如 m.x 相當(dāng)于 m.__dict__["x"]。 模塊對(duì)象不包含用于初始化模塊的代碼對(duì)象(因?yàn)槌跏蓟瓿珊蟛恍枰?/p>

屬性賦值會(huì)更新模塊的命名空間字典,例如 m.x = 1 等同于 m.__dict__["x"] = 1

預(yù)定義的 (可寫) 屬性: __name__ 為模塊的名稱; __doc__ 為模塊的文檔字符串,如果沒有則為 None; __annotations__ (可選) 為一個(gè)包含 變量標(biāo)注 的字典,它是在模塊體執(zhí)行時(shí)獲取的; __file__ 是模塊對(duì)應(yīng)的被加載文件的路徑名,如果它是加載自一個(gè)文件的話。某些類型的模塊可能沒有 __file__ 屬性,例如 C 模塊是靜態(tài)鏈接到解釋器內(nèi)部的; 對(duì)于從一個(gè)共享庫動(dòng)態(tài)加載的擴(kuò)展模塊來說該屬性為該共享庫文件的路徑名。

特殊的只讀屬性: __dict__ 為以字典對(duì)象表示的模塊命名空間。

CPython implementation detail: 由于 CPython 清理模塊字典的設(shè)定,當(dāng)模塊離開作用域時(shí)模塊字典將會(huì)被清理,即使該字典還有活動(dòng)的引用。想避免此問題,可復(fù)制該字典或保持模塊狀態(tài)以直接使用其字典。

自定義類

自定義類這種類型一般通過類定義來創(chuàng)建 (參見 類定義 一節(jié))。每個(gè)類都有通過一個(gè)字典對(duì)象實(shí)現(xiàn)的獨(dú)立命名空間。類屬性引用會(huì)被轉(zhuǎn)化為在此字典中查找,例如 C.x 會(huì)被轉(zhuǎn)化為 C.__dict__["x"] (不過也存在一些鉤子對(duì)象以允許其他定位屬性的方式)。當(dāng)未在其中發(fā)現(xiàn)某個(gè)屬性名稱時(shí),會(huì)繼續(xù)在基類中查找。這種基類查找使用 C3 方法解析順序,即使存在 '鉆石形' 繼承結(jié)構(gòu)即有多條繼承路徑連到一個(gè)共同祖先也能保持正確的行為。有關(guān) Python 使用的 C3 MRO 的詳情可查看配合 2.3 版發(fā)布的文檔 https://www.python.org/download/releases/2.3/mro/.

當(dāng)一個(gè)類屬性引用 (假設(shè)類名為 C) 會(huì)產(chǎn)生一個(gè)類方法對(duì)象時(shí),它將轉(zhuǎn)化為一個(gè) __self__ 屬性為 C 的實(shí)例方法對(duì)象。當(dāng)其會(huì)產(chǎn)生一個(gè)靜態(tài)方法對(duì)象時(shí),它將轉(zhuǎn)化為該靜態(tài)方法對(duì)象所封裝的對(duì)象。從類的 __dict__ 所包含內(nèi)容以外獲取屬性的其他方式請(qǐng)參看 實(shí)現(xiàn)描述器 一節(jié)。

類屬性賦值會(huì)更新類的字典,但不會(huì)更新基類的字典。

類對(duì)象可被調(diào)用 (見上文) 以產(chǎn)生一個(gè)類實(shí)例 (見下文)。

特殊屬性: __name__ 為類的名稱; __module__ 為類所在模塊的名稱; __dict__ 為包含類命名空間的字典; __bases__ 為包含基類的元組,按其在基類列表中的出現(xiàn)順序排列; __doc__ 為類的文檔字符串,如果沒有則為 None; __annotations__ (可選) 為一個(gè)包含 變量標(biāo)注 的字典,它是在類體執(zhí)行時(shí)獲取的。

類實(shí)例

類實(shí)例可通過調(diào)用類對(duì)象來創(chuàng)建 (見上文)。每個(gè)類實(shí)例都有通過一個(gè)字典對(duì)象實(shí)現(xiàn)的獨(dú)立命名空間,屬性引用會(huì)首先在此字典中查找。當(dāng)未在其中發(fā)現(xiàn)某個(gè)屬性,而實(shí)例對(duì)應(yīng)的類中有該屬性時(shí),會(huì)繼續(xù)在類屬性中查找。如果找到的類屬性為一個(gè)用戶定義函數(shù)對(duì)象,它會(huì)被轉(zhuǎn)化為實(shí)例方法對(duì)象,其 __self__ 屬性即該實(shí)例。靜態(tài)方法和類方法對(duì)象也會(huì)被轉(zhuǎn)化;參見上文 "Classes" 一節(jié)。要了解其他通過類實(shí)例來獲取相應(yīng)類屬性的方式可參見 實(shí)現(xiàn)描述器 一節(jié),這樣得到的屬性可能與實(shí)際存放于類的 __dict__ 中的對(duì)象不同。如果未找到類屬性,而對(duì)象對(duì)應(yīng)的類具有 __getattr__() 方法,則會(huì)調(diào)用該方法來滿足查找要求。

屬性賦值和刪除會(huì)更新實(shí)例的字典,但不會(huì)更新對(duì)應(yīng)類的字典。如果類具有 __setattr__()__delattr__() 方法,則將調(diào)用方法而不再直接更新實(shí)例的字典。

如果類實(shí)例具有某些特殊名稱的方法,就可以偽裝為數(shù)字、序列或映射。參見 特殊方法名稱 一節(jié)。

特殊屬性: __dict__ 為屬性字典; __class__ 為實(shí)例對(duì)應(yīng)的類。

I/O 對(duì)象 (或稱文件對(duì)象)

file object 表示一個(gè)打開的文件。有多種快捷方式可用來創(chuàng)建文件對(duì)象: open() 內(nèi)置函數(shù),以及 os.popen(), os.fdopen() 和 socket 對(duì)象的 makefile() 方法 (還可能使用某些擴(kuò)展模塊所提供的其他函數(shù)或方法)。

sys.stdin, sys.stdoutsys.stderr 會(huì)初始化為對(duì)應(yīng)于解釋器標(biāo)準(zhǔn)輸入、輸出和錯(cuò)誤流的文件對(duì)象;它們都會(huì)以文本模式打開,因此都遵循 io.TextIOBase 抽象類所定義的接口。

內(nèi)部類型

某些由解釋器內(nèi)部使用的類型也被暴露給用戶。它們的定義可能隨未來解釋器版本的更新而變化,為內(nèi)容完整起見在此處一并介紹。

代碼對(duì)象

代碼對(duì)象表示 編譯為字節(jié)的 可執(zhí)行 Python 代碼,或稱 bytecode。代碼對(duì)象和函數(shù)對(duì)象的區(qū)別在于函數(shù)對(duì)象包含對(duì)函數(shù)全局對(duì)象 (函數(shù)所屬的模塊) 的顯式引用,而代碼對(duì)象不包含上下文;而且默認(rèn)參數(shù)值會(huì)存放于函數(shù)對(duì)象而不是代碼對(duì)象內(nèi) (因?yàn)樗鼈儽硎驹谶\(yùn)行時(shí)算出的值)。與函數(shù)對(duì)象不同,代碼對(duì)象不可變,也不包含對(duì)可變對(duì)象的引用 (不論是直接還是間接)。

特殊的只讀屬性: co_name 為函數(shù)名稱; co_argcount 為位置參數(shù)的數(shù)量 (包括有默認(rèn)值的參數(shù)); co_nlocals 為函數(shù)使用的局部變量數(shù)量 (包括參數(shù)); co_varnames 為一個(gè)包括局部變量名稱的元組 (以參數(shù)名打頭); co_cellvars 為一個(gè)包含被嵌套函數(shù)所引用的局部變量名稱的元組; co_freevars 為一個(gè)包含自由變量名稱的元組; co_code 為一個(gè)表示字節(jié)碼指令序列的字符串; co_consts 為一個(gè)包含字節(jié)碼所使用的字面值的元組; co_names 為一個(gè)包含字節(jié)碼所使用的名稱的元組; co_filename 為被編碼代碼所在的文件名; co_firstlineno 為函數(shù)首行的行號(hào); co_lnotab 為一個(gè)以編碼表示的從字節(jié)碼偏移量到行號(hào)的映射的字符串 (詳情參見解釋器的源碼); co_stacksize 為要求的棧大小; co_flags 為一個(gè)以整數(shù)編碼表示的多個(gè)解釋器所用的旗標(biāo)。

以下是可用于 co_flags 的標(biāo)志位定義:如果函數(shù)使用 *arguments 語法來接受任意數(shù)量的位置參數(shù),則 0x04 位被設(shè)置;如果函數(shù)使用 **keywords 語法來接受任意數(shù)量的關(guān)鍵字參數(shù),則 0x08 位被設(shè)置;如果函數(shù)是一個(gè)生成器,則 0x20 位被設(shè)置。

未來特性聲明 (from __future__ import division) 也使用 co_flags 中的標(biāo)志位來指明代碼對(duì)象的編譯是否啟用特定的特性: 如果函數(shù)編譯時(shí)啟用未來除法特性則設(shè)置 0x2000 位; 在更早的 Python 版本中則使用 0x100x1000 位。

co_flags 中的其他位被保留為內(nèi)部使用。

如果代碼對(duì)象表示一個(gè)函數(shù),co_consts 中的第一項(xiàng)將是函數(shù)的文檔字符串,如果未定義則為 None。

幀對(duì)象

幀對(duì)象表示執(zhí)行幀。它們可能出現(xiàn)在回溯對(duì)象中 (見下文),還會(huì)被傳遞給注冊(cè)跟蹤函數(shù)。

特殊的只讀屬性: f_back 為前一堆棧幀 (指向調(diào)用者),如是最底層堆棧幀則為 None; f_code 為此幀中所執(zhí)行的代碼對(duì)象; f_locals 為用于查找本地變量的字典; f_globals 則用于查找全局變量; f_builtins 用于查找內(nèi)置 (固有) 名稱; f_lasti 給出精確指令 (這是代碼對(duì)象的字節(jié)碼字符串的一個(gè)索引)。

特殊的可寫屬性: f_trace,如果不為 None,則是在代碼執(zhí)行期間調(diào)用各類事件的函數(shù) (由調(diào)試器使用)。通常每個(gè)新源碼行會(huì)觸發(fā)一個(gè)事件 - 這可以通過將 f_trace_lines 設(shè)為 False 來禁用。

具體的實(shí)現(xiàn) 可能 會(huì)通過將 f_trace_opcodes 設(shè)為 True 來允許按操作碼請(qǐng)求事件。請(qǐng)注意如果跟蹤函數(shù)引發(fā)的異常逃逸到被跟蹤的函數(shù)中,這可能會(huì)導(dǎo)致未定義的解釋器行為。

f_lineno 為幀的當(dāng)前行號(hào) --- 在這里寫入從一個(gè)跟蹤函數(shù)內(nèi)部跳轉(zhuǎn)的指定行 (僅用于最底層的幀)。調(diào)試器可以通過寫入 f_lineno 實(shí)現(xiàn)一個(gè) Jump 命令 (即設(shè)置下一語句)。

幀對(duì)象支持一個(gè)方法:

frame.clear()?

此方法清除該幀持有的全部對(duì)本地變量的引用。而且如果該幀屬于一個(gè)生成器,生成器會(huì)被完成。這有助于打破包含幀對(duì)象的循環(huán)引用 (例如當(dāng)捕獲一個(gè)異常并保存其回溯在之后使用)。

如果該幀當(dāng)前正在執(zhí)行則會(huì)引發(fā) RuntimeError

3.4 新版功能.

回溯對(duì)象

回溯對(duì)象表示一個(gè)異常的棧跟蹤記錄。當(dāng)異常發(fā)生時(shí)會(huì)隱式地創(chuàng)建一個(gè)回溯對(duì)象,也可能通過調(diào)用 types.TracebackType 顯式地創(chuàng)建。

對(duì)于隱式地創(chuàng)建的回溯對(duì)象,當(dāng)查找異常句柄使得執(zhí)行棧展開時(shí),會(huì)在每個(gè)展開層級(jí)的當(dāng)前回溯之前插入一個(gè)回溯對(duì)象。當(dāng)進(jìn)入一個(gè)異常句柄時(shí),棧跟蹤將對(duì)程序啟用。(參見 try 語句 一節(jié)。) 它可作為 sys.exc_info() 所返回的元組的第三項(xiàng),以及所捕獲異常的 __traceback__ 屬性被獲取。

當(dāng)程序不包含可用的句柄時(shí),棧跟蹤會(huì) (以良好的格式) 寫入標(biāo)準(zhǔn)錯(cuò)誤流;如果解釋器處于交互模式,它也可作為 sys.last_traceback 對(duì)用戶啟用。

對(duì)于顯式創(chuàng)建的回溯對(duì)象,則由回溯對(duì)象的創(chuàng)建者來決定應(yīng)該如何鏈接 tb_next 屬性來構(gòu)成完整的棧跟蹤。

特殊的只讀屬性: tb_frame 指向當(dāng)前層級(jí)的執(zhí)行幀; tb_lineno 給出發(fā)生異常所在的行號(hào); tb_lasti 標(biāo)示具體指令。如果異常發(fā)生于沒有匹配的 except 子句或有 finally 子句的 try 語句中,回溯對(duì)象中的行號(hào)和最后指令可能與相應(yīng)幀對(duì)象中行號(hào)不同。

特殊的可寫屬性: tb_next 為棧跟蹤中的下一層級(jí) (通往發(fā)生異常的幀),如果沒有下一層級(jí)則為 None

在 3.7 版更改: 回溯對(duì)象現(xiàn)在可以使用 Python 代碼顯式地實(shí)例化,現(xiàn)有實(shí)例的 tb_next 屬性可以被更新。

切片對(duì)象

切片對(duì)象用來表示 __getitem__() 方法用到的切片。 該對(duì)象也可使用內(nèi)置的 slice() 函數(shù)來創(chuàng)建。

特殊的只讀屬性: start 為下界; stop 為上界; step 為步長值; 各值如省略則為 None。這些屬性可具有任意類型。

切片對(duì)象支持一個(gè)方法:

slice.indices(self, length)?

此方法接受一個(gè)整型參數(shù) length 并計(jì)算在切片對(duì)象被應(yīng)用到 length 指定長度的條目序列時(shí)切片的相關(guān)信息應(yīng)如何描述。 其返回值為三個(gè)整型數(shù)組成的元組;這些數(shù)分別為切片的 startstop 索引號(hào)以及 step 步長值。索引號(hào)缺失或越界則按照與正規(guī)切片相一致的方式處理。

靜態(tài)方法對(duì)象

靜態(tài)方法對(duì)象提供了一種避免上文所述將函數(shù)對(duì)象轉(zhuǎn)換為方法對(duì)象的方式。靜態(tài)方法對(duì)象為對(duì)任意其他對(duì)象的封裝,通常用來封裝用戶定義方法對(duì)象。當(dāng)從類或類實(shí)例獲取一個(gè)靜態(tài)方法對(duì)象時(shí),實(shí)際返回的對(duì)象是封裝的對(duì)象,它不會(huì)被進(jìn)一步轉(zhuǎn)換。靜態(tài)方法對(duì)象自身不是可調(diào)用的,但它們所封裝的對(duì)象通常都是可調(diào)用的。靜態(tài)方法對(duì)象可通過內(nèi)置的 staticmethod() 構(gòu)造器來創(chuàng)建。

類方法對(duì)象

類方法對(duì)象和靜態(tài)方法一樣是對(duì)其他對(duì)象的封裝,會(huì)改變從類或類實(shí)例獲取該對(duì)象的方式。類方法對(duì)象在此類獲取操作中的行為已在上文 "用戶定義方法" 一節(jié)中描述。類方法對(duì)象可通過內(nèi)置的 classmethod() 構(gòu)造器來創(chuàng)建。

3.3. 特殊方法名稱?

一個(gè)類可以通過定義具有特殊名稱的方法來實(shí)現(xiàn)由特殊語法所引發(fā)的特定操作 (例如算術(shù)運(yùn)算或下標(biāo)與切片)。這是 Python 實(shí)現(xiàn) 操作符重載 的方式,允許每個(gè)類自行定義基于操作符的特定行為。例如,如果一個(gè)類定義了名為 __getitem__() 的方法,并且 x 為該類的一個(gè)實(shí)例,則 x[i] 基本就等同于 type(x).__getitem__(x, i)。除非有說明例外情況,在沒有定義適當(dāng)方法的情況下嘗試執(zhí)行一種操作將引發(fā)一個(gè)異常 (通常為 AttributeErrorTypeError)。

將一個(gè)特殊方法設(shè)為 None 表示對(duì)應(yīng)的操作不可用。例如,如果一個(gè)類將 __iter__() 設(shè)為 None,則該類就是不可迭代的,因此對(duì)其實(shí)例調(diào)用 iter() 將引發(fā)一個(gè) TypeError (而不會(huì)回退至 __getitem__()). 2

在實(shí)現(xiàn)模擬任何內(nèi)置類型的類時(shí),很重要的一點(diǎn)是模擬的實(shí)現(xiàn)程度對(duì)于被模擬對(duì)象來說應(yīng)當(dāng)是有意義的。例如,提取單個(gè)元素的操作對(duì)于某些序列來說是適宜的,但提取切片可能就沒有意義。(這種情況的一個(gè)實(shí)例是 W3C 的文檔對(duì)象模型中的 NodeList 接口。)

3.3.1. 基本定制?

object.__new__(cls[, ...])?

調(diào)用以創(chuàng)建一個(gè) cls 類的新實(shí)例。__new__() 是一個(gè)靜態(tài)方法 (因?yàn)槭翘乩阅悴恍枰@式地聲明),它會(huì)將所請(qǐng)求實(shí)例所屬的類作為第一個(gè)參數(shù)。其余的參數(shù)會(huì)被傳遞給對(duì)象構(gòu)造器表達(dá)式 (對(duì)類的調(diào)用)。__new__() 的返回值應(yīng)為新對(duì)象實(shí)例 (通常是 cls 的實(shí)例)。

典型的實(shí)現(xiàn)會(huì)附帶適宜的參數(shù)使用 super().__new__(cls[, ...]),通過超類的 __new__() 方法來創(chuàng)建一個(gè)類的新實(shí)例,然后根據(jù)需要修改新創(chuàng)建的實(shí)例再將其返回。

如果 __new__() 返回一個(gè) cls 的實(shí)例,則新實(shí)例的 __init__() 方法會(huì)在之后被執(zhí)行,例如 __init__(self[, ...]),其中 self 為新實(shí)例,其余的參數(shù)與被傳遞給 __new__() 的相同。

如果 __new__() 未返回一個(gè) cls 的實(shí)例,則新實(shí)例的 __init__() 方法就不會(huì)被執(zhí)行。

__new__() 的目的主要是允許不可變類型的子類 (例如 int, str 或 tuple) 定制實(shí)例創(chuàng)建過程。它也常會(huì)在自定義元類中被重載以便定制類創(chuàng)建過程。

object.__init__(self[, ...])?

在實(shí)例 (通過 __new__()) 被創(chuàng)建之后,返回調(diào)用者之前調(diào)用。其參數(shù)與傳遞給類構(gòu)造器表達(dá)式的參數(shù)相同。一個(gè)基類如果有 __init__() 方法,則其所派生的類如果也有 __init__() 方法,就必須顯式地調(diào)用它以確保實(shí)例基類部分的正確初始化;例如: super().__init__([args...]).

因?yàn)閷?duì)象是由 __new__()__init__() 協(xié)作構(gòu)造完成的 (由 __new__() 創(chuàng)建,并由 __init__() 定制),所以 __init__() 返回的值只能是 None,否則會(huì)在運(yùn)行時(shí)引發(fā) TypeError

object.__del__(self)?

在實(shí)例將被銷毀時(shí)調(diào)用。 這還被稱為終結(jié)器或析構(gòu)器(不適當(dāng))。 如果一個(gè)基類具有 __del__() 方法,則其所派生的類如果也有 __del__() 方法,就必須顯式地調(diào)用它以確保實(shí)例基類部分的正確清除。

__del__() 方法可以 (但不推薦!) 通過創(chuàng)建一個(gè)該實(shí)例的新引用來推遲其銷毀。這被稱為對(duì)象 重生__del__() 是否會(huì)在重生的對(duì)象將被銷毀時(shí)再次被調(diào)用是由具體實(shí)現(xiàn)決定的 ;當(dāng)前的 CPython 實(shí)現(xiàn)只會(huì)調(diào)用一次。

當(dāng)解釋器退出時(shí)不會(huì)確保為仍然存在的對(duì)象調(diào)用 __del__() 方法。

注解

del x 并不直接調(diào)用 x.__del__() --- 前者會(huì)將 x 的引用計(jì)數(shù)減一,而后者僅會(huì)在 x 的引用計(jì)數(shù)變?yōu)榱銜r(shí)被調(diào)用。

CPython implementation detail: It is possible for a reference cycle to prevent the reference count of an object from going to zero. In this case, the cycle will be later detected and deleted by the cyclic garbage collector. A common cause of reference cycles is when an exception has been caught in a local variable. The frame's locals then reference the exception, which references its own traceback, which references the locals of all frames caught in the traceback.

參見

gc 模塊的文檔。

警告

由于調(diào)用 __del__() 方法時(shí)周邊狀況已不確定,在其執(zhí)行期間發(fā)生的異常將被忽略,改為打印一個(gè)警告到 sys.stderr。特別地:

  • __del__() 可在任意代碼被執(zhí)行時(shí)啟用,包括來自任意線程的代碼。如果 __del__() 需要接受鎖或啟用其他阻塞資源,可能會(huì)發(fā)生死鎖,例如該資源已被為執(zhí)行 __del__() 而中斷的代碼所獲取。

  • __del__() 可以在解釋器關(guān)閉階段被執(zhí)行。因此,它需要訪問的全局變量(包含其他模塊)可能已被刪除或設(shè)為 None。Python 會(huì)保證先刪除模塊中名稱以單個(gè)下劃線打頭的全局變量再刪除其他全局變量;如果已不存在其他對(duì)此類全局變量的引用,這有助于確保導(dǎo)入的模塊在 __del__() 方法被調(diào)用時(shí)仍然可用。

object.__repr__(self)?

repr() 內(nèi)置函數(shù)調(diào)用以輸出一個(gè)對(duì)象的“官方”字符串表示。如果可能,這應(yīng)類似一個(gè)有效的 Python 表達(dá)式,能被用來重建具有相同取值的對(duì)象(只要有適當(dāng)?shù)沫h(huán)境)。如果這不可能,則應(yīng)返回形式如 <...some useful description...> 的字符串。返回值必須是一個(gè)字符串對(duì)象。如果一個(gè)類定義了 __repr__() 但未定義 __str__(),則在需要該類的實(shí)例的“非正式”字符串表示時(shí)也會(huì)使用 __repr__()

此方法通常被用于調(diào)試,因此確保其表示的內(nèi)容包含豐富信息且無歧義是很重要的。

object.__str__(self)?

通過 str(object) 以及內(nèi)置函數(shù) format()print() 調(diào)用以生成一個(gè)對(duì)象的“非正式”或格式良好的字符串表示。返回值必須為一個(gè) 字符串 對(duì)象。

此方法與 object.__repr__() 的不同點(diǎn)在于 __str__() 并不預(yù)期返回一個(gè)有效的 Python 表達(dá)式:可以使用更方便或更準(zhǔn)確的描述信息。

內(nèi)置類型 object 所定義的默認(rèn)實(shí)現(xiàn)會(huì)調(diào)用 object.__repr__()

object.__bytes__(self)?

通過 bytes 調(diào)用以生成一個(gè)對(duì)象的字節(jié)串表示。這應(yīng)該返回一個(gè) bytes 對(duì)象。

object.__format__(self, format_spec)?

通過 format() 內(nèi)置函數(shù)、擴(kuò)展、格式化字符串字面值 的求值以及 str.format() 方法調(diào)用以生成一個(gè)對(duì)象的“格式化”字符串表示。 format_spec 參數(shù)為包含所需格式選項(xiàng)描述的字符串。 format_spec 參數(shù)的解讀是由實(shí)現(xiàn) __format__() 的類型決定的,不過大多數(shù)類或是將格式化委托給某個(gè)內(nèi)置類型,或是使用相似的格式化選項(xiàng)語法。

請(qǐng)參看 格式規(guī)格迷你語言 了解標(biāo)準(zhǔn)格式化語法的描述。

返回值必須為一個(gè)字符串對(duì)象。

在 3.4 版更改: object 本身的 __format__ 方法如果被傳入任何非空字符,將會(huì)引發(fā)一個(gè) TypeError

在 3.7 版更改: object.__format__(x, '') 現(xiàn)在等同于 str(x) 而不再是 format(str(self), '')。

object.__lt__(self, other)?
object.__le__(self, other)?
object.__eq__(self, other)?
object.__ne__(self, other)?
object.__gt__(self, other)?
object.__ge__(self, other)?

以上這些被稱為“富比較”方法。運(yùn)算符號(hào)與方法名稱的對(duì)應(yīng)關(guān)系如下:x<y 調(diào)用 x.__lt__(y)、x<=y 調(diào)用 x.__le__(y)、x==y 調(diào)用 x.__eq__(y)x!=y 調(diào)用 x.__ne__(y)、x>y 調(diào)用 x.__gt__(y)、x>=y 調(diào)用 x.__ge__(y)

如果指定的參數(shù)對(duì)沒有相應(yīng)的實(shí)現(xiàn),富比較方法可能會(huì)返回單例對(duì)象 NotImplemented。按照慣例,成功的比較會(huì)返回 FalseTrue。不過實(shí)際上這些方法可以返回任意值,因此如果比較運(yùn)算符是要用于布爾值判斷(例如作為 if 語句的條件),Python 會(huì)對(duì)返回值調(diào)用 bool() 以確定結(jié)果為真還是假。

在默認(rèn)情況下 __ne__() 會(huì)委托給 __eq__() 并將結(jié)果取反,除非結(jié)果為 NotImplemented。比較運(yùn)算符之間沒有其他隱含關(guān)系,例如 (x<y or x==y) 為真并不意味著 x<=y。要根據(jù)單根運(yùn)算自動(dòng)生成排序操作,請(qǐng)參看 functools.total_ordering()。

請(qǐng)查看 __hash__() 的相關(guān)段落,了解創(chuàng)建可支持自定義比較運(yùn)算并可用作字典鍵的 hashable 對(duì)象時(shí)要注意的一些事項(xiàng)。

這些方法并沒有對(duì)調(diào)參數(shù)版本(在左邊參數(shù)不支持該操作但右邊參數(shù)支持時(shí)使用);而是 __lt__()__gt__() 互為對(duì)方的反射, __le__()__ge__() 互為對(duì)方的反射,而 __eq__()__ne__() 則是它們自己的反射。如果兩個(gè)操作數(shù)的類型不同,且右操作數(shù)類型是左操作數(shù)類型的直接或間接子類,則優(yōu)先選擇右操作數(shù)的反射方法,否則優(yōu)先選擇左操作數(shù)的方法。虛擬子類不會(huì)被考慮。

object.__hash__(self)?

通過內(nèi)置函數(shù) hash() 調(diào)用以對(duì)哈希集的成員進(jìn)行操作,屬于哈希集的類型包括 setfrozenset 以及 dict。__hash__() 應(yīng)該返回一個(gè)整數(shù)。對(duì)象比較結(jié)果相同所需的唯一特征屬性是其具有相同的哈希值;建議的做法是把參與比較的對(duì)象全部組件的哈希值混在一起,即將它們打包為一個(gè)元組并對(duì)該元組做哈希運(yùn)算。例如:

def __hash__(self):
    return hash((self.name, self.nick, self.color))

注解

hash() 會(huì)從一個(gè)對(duì)象自定義的 __hash__() 方法返回值中截?cái)酁?Py_ssize_t 的大小。通常對(duì) 64 位構(gòu)建為 8 字節(jié),對(duì) 32 位構(gòu)建為 4 字節(jié)。如果一個(gè)對(duì)象的 __hash__() 必須在不同位大小的構(gòu)建上進(jìn)行互操作,請(qǐng)確保檢查全部所支持構(gòu)建的寬度。做到這一點(diǎn)的簡單方法是使用 python -c "import sys; print(sys.hash_info.width)"。

如果一個(gè)類沒有定義 __eq__() 方法,那么也不應(yīng)該定義 __hash__() 操作;如果它定義了 __eq__() 但沒有定義 __hash__(),則其實(shí)例將不可被用作可哈希集的項(xiàng)。如果一個(gè)類定義了可變對(duì)象并實(shí)現(xiàn)了 __eq__() 方法,則不應(yīng)該實(shí)現(xiàn) __hash__(),因?yàn)榭晒<膶?shí)現(xiàn)要求鍵的哈希集是不可變的(如果對(duì)象的哈希值發(fā)生改變,它將處于錯(cuò)誤的哈希桶中)。

用戶定義的類默認(rèn)帶有 __eq__()__hash__() 方法;使用它們與任何對(duì)象(自己除外)比較必定不相等,并且 x.__hash__() 會(huì)返回一個(gè)恰當(dāng)?shù)闹狄源_保 x == y 同時(shí)意味著 x is yhash(x) == hash(y)。

一個(gè)類如果重載了 __eq__() 且沒有定義 __hash__() 則會(huì)將其 __hash__() 隱式地設(shè)為 None。當(dāng)一個(gè)類的 __hash__() 方法為 None 時(shí),該類的實(shí)例將在一個(gè)程序嘗試獲取其哈希值時(shí)正確地引發(fā) TypeError,并會(huì)在檢測 isinstance(obj, collections.abc.Hashable) 時(shí)被正確地識(shí)別為不可哈希對(duì)象。

如果一個(gè)重載了 __eq__() 的類需要保留來自父類的 __hash__() 實(shí)現(xiàn),則必須通過設(shè)置 __hash__ = <ParentClass>.__hash__ 來顯式地告知解釋器。

如果一個(gè)沒有重載 __eq__() 的類需要去掉哈希支持,則應(yīng)該在類定義中包含 __hash__ = None。一個(gè)自定義了 __hash__() 以顯式地引發(fā) TypeError 的類會(huì)被 isinstance(obj, collections.abc.Hashable) 調(diào)用錯(cuò)誤地識(shí)別為可哈希對(duì)象。

注解

在默認(rèn)情況下,str、bytes 和 datetime 對(duì)象的 __hash__() 值會(huì)使用一個(gè)不可預(yù)知的隨機(jī)值“加鹽”。雖然它們會(huì)在一個(gè)單獨(dú) Python 進(jìn)程中保持不變,它們的哈希值在重復(fù)運(yùn)行的 Python 之間是不可預(yù)測的。

這種做法是為了防止以下形式的拒絕服務(wù)攻擊:通過仔細(xì)選擇輸入來利用字典插入操作在最壞情況下的執(zhí)行效率即 O(n^2) 復(fù)雜度。詳情見 http://www.ocert.org/advisories/ocert-2011-003.html

改變哈希值會(huì)影響集合的迭代次序。Python 也從不保證這個(gè)次序不會(huì)被改變(通常它在 32 位和 64 位構(gòu)建上是不一致的)。

另見 PYTHONHASHSEED.

在 3.3 版更改: 默認(rèn)啟用哈希隨機(jī)化。

object.__bool__(self)?

調(diào)用此方法以實(shí)現(xiàn)真值檢測以及內(nèi)置的 bool() 操作;應(yīng)該返回 FalseTrue。如果未定義此方法,則會(huì)查找并調(diào)用 __len__() 并在其返回非零值時(shí)視對(duì)象的邏輯值為真。如果一個(gè)類既未定義 __len__() 也未定義 __bool__() 則視其所有實(shí)例的邏輯值為真。

3.3.2. 自定義屬性訪問?

可以定義下列方法來自定義對(duì)類實(shí)例屬性訪問(x.name 的使用、賦值或刪除)的具體含義.

object.__getattr__(self, name)?

當(dāng)默認(rèn)屬性訪問因引發(fā) AttributeError 而失敗時(shí)被調(diào)用 (可能是調(diào)用 __getattribute__() 時(shí)由于 name 不是一個(gè)實(shí)例屬性或 self 的類關(guān)系樹中的屬性而引發(fā)了 AttributeError;或者是對(duì) name 特性屬性調(diào)用 __get__() 時(shí)引發(fā)了 AttributeError)。此方法應(yīng)當(dāng)返回(找到的)屬性值或是引發(fā)一個(gè) AttributeError 異常。

請(qǐng)注意如果屬性是通過正常機(jī)制找到的,__getattr__() 就不會(huì)被調(diào)用。(這是在 __getattr__()__setattr__() 之間故意設(shè)置的不對(duì)稱性。)這既是出于效率理由也是因?yàn)椴贿@樣設(shè)置的話 __getattr__() 將無法訪問實(shí)例的其他屬性。要注意至少對(duì)于實(shí)例變量來說,你不必在實(shí)例屬性字典中插入任何值(而是通過插入到其他對(duì)象)就可以模擬對(duì)它的完全控制。請(qǐng)參閱下面的 __getattribute__() 方法了解真正獲取對(duì)屬性訪問的完全控制權(quán)的辦法。

object.__getattribute__(self, name)?

此方法會(huì)無條件地被調(diào)用以實(shí)現(xiàn)對(duì)類實(shí)例屬性的訪問。如果類還定義了 __getattr__(),則后者不會(huì)被調(diào)用,除非 __getattribute__() 顯式地調(diào)用它或是引發(fā)了 AttributeError。此方法應(yīng)當(dāng)返回(找到的)屬性值或是引發(fā)一個(gè) AttributeError 異常。為了避免此方法中的無限遞歸,其實(shí)現(xiàn)應(yīng)該總是調(diào)用具有相同名稱的基類方法來訪問它所需要的任何屬性,例如 object.__getattribute__(self, name)

注解

此方法在作為通過特定語法或內(nèi)置函數(shù)隱式地調(diào)用的結(jié)果的情況下查找特殊方法時(shí)仍可能會(huì)被跳過。參見 特殊方法查找。

object.__setattr__(self, name, value)?

此方法在一個(gè)屬性被嘗試賦值時(shí)被調(diào)用。這個(gè)調(diào)用會(huì)取代正常機(jī)制(即將值保存到實(shí)例字典)。 name 為屬性名稱, value 為要賦給屬性的值。

如果 __setattr__() 想要賦值給一個(gè)實(shí)例屬性,它應(yīng)該調(diào)用同名的基類方法,例如 object.__setattr__(self, name, value)。

object.__delattr__(self, name)?

類似于 __setattr__() 但其作用為刪除而非賦值。此方法應(yīng)該僅在 del obj.name 對(duì)于該對(duì)象有意義時(shí)才被實(shí)現(xiàn)。

object.__dir__(self)?

此方法會(huì)在對(duì)相應(yīng)對(duì)象調(diào)用 dir() 時(shí)被調(diào)用。返回值必須為一個(gè)序列。 dir() 會(huì)把返回的序列轉(zhuǎn)換為列表并對(duì)其排序。

3.3.2.1. 自定義模塊屬性訪問?

特殊名稱 __getattr____dir__ 還可被用來自定義對(duì)模塊屬性的訪問。模塊層級(jí)的 __getattr__ 函數(shù)應(yīng)當(dāng)接受一個(gè)參數(shù),其名稱為一個(gè)屬性名,并返回計(jì)算結(jié)果值或引發(fā)一個(gè) AttributeError。如果通過正常查找即 object.__getattribute__() 未在模塊對(duì)象中找到某個(gè)屬性,則 __getattr__ 會(huì)在模塊的 __dict__ 中查找,未找到時(shí)會(huì)引發(fā)一個(gè) AttributeError。如果找到,它會(huì)以屬性名被調(diào)用并返回結(jié)果值。

__dir__ 函數(shù)應(yīng)當(dāng)不接受任何參數(shù),并且返回一個(gè)表示模塊中可訪問名稱的字符串序列。 此函數(shù)如果存在,將會(huì)重載一個(gè)模塊中的標(biāo)準(zhǔn) dir() 查找。

想要更細(xì)致地自定義模塊的行為(設(shè)置屬性和特性屬性等待),可以將模塊對(duì)象的 __class__ 屬性設(shè)置為一個(gè) 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__ 和設(shè)置模塊的 __class__ 只會(huì)影響使用屬性訪問語法進(jìn)行的查找 -- 直接訪問模塊全局變量(不論是通過模塊內(nèi)的代碼還是通過對(duì)模塊全局字典的引用)是不受影響的。

在 3.5 版更改: __class__ 模塊屬性改為可寫。

3.7 新版功能: __getattr____dir__ 模塊屬性。

參見

PEP 562 - 模塊 __getattr__ 和 __dir__

描述用于模塊的 __getattr____dir__ 函數(shù)。

3.3.2.2. 實(shí)現(xiàn)描述器?

以下方法僅當(dāng)一個(gè)包含該方法的類(稱為 描述器 類)的實(shí)例出現(xiàn)于一個(gè) 所有者 類中的時(shí)候才會(huì)起作用(該描述器必須在所有者類或其某個(gè)上級(jí)類的字典中)。在以下示例中,“屬性”指的是名稱為所有者類 __dict__ 中的特征屬性的鍵名的屬性。

object.__get__(self, instance, owner)?

調(diào)用此方法以獲取所有者類的屬性(類屬性訪問)或該類的實(shí)例的屬性(實(shí)例屬性訪問)。所有者 是指所有者類,而 實(shí)例 是指被用來訪問屬性的實(shí)例,如果是 所有者 被用來訪問屬性時(shí)則為 None。此方法應(yīng)當(dāng)返回(計(jì)算出的)屬性值或是引發(fā)一個(gè) AttributeError 異常。

object.__set__(self, instance, value)?

調(diào)用此方法以設(shè)置 instance 指定的所有者類的實(shí)例的屬性為新值 value。

object.__delete__(self, instance)?

調(diào)用此方法以刪除 instance 指定的所有者類的實(shí)例的屬性。

object.__set_name__(self, owner, name)?

在所有者類 owner 創(chuàng)建時(shí)被調(diào)用。描述器會(huì)被賦值給 name

注解

__set_name__() 只是作為 type 構(gòu)造器的一部分被隱式地調(diào)用,因此在某個(gè)類被初次創(chuàng)建之后又額外添加一個(gè)描述器時(shí),那就需要顯式地調(diào)用它并且附帶適當(dāng)?shù)男螀?

class A:
   pass
descr = custom_descriptor()
A.attr = descr
descr.__set_name__(A, 'attr')

詳情參見 創(chuàng)建類對(duì)象。

3.6 新版功能.

屬性 __objclass__ 會(huì)被 inspect 模塊解讀為指定此對(duì)象定義所在的類(正確設(shè)置此屬性有助于動(dòng)態(tài)類屬性的運(yùn)行時(shí)內(nèi)?。?。對(duì)于可調(diào)用對(duì)象來說,它可以指明預(yù)期或要求提供一個(gè)特定類型(或子類)的實(shí)例作為第一個(gè)位置參數(shù)(例如,CPython 會(huì)為實(shí)現(xiàn)于 C 中的未綁定方法設(shè)置此屬性)。

3.3.2.3. 發(fā)起調(diào)用描述器?

總的說來,描述器就是具有“綁定行為”的對(duì)象屬性,其屬性訪問已被描述器協(xié)議中的方法所重載,包括 __get__(), __set__()__delete__()。如果一個(gè)對(duì)象定義了以上方法中的任意一個(gè),它就被稱為描述器。

屬性訪問的默認(rèn)行為是從一個(gè)對(duì)象的字典中獲取、設(shè)置或刪除屬性。例如,a.x 的查找順序會(huì)從 a.__dict__['x'] 開始,然后是 type(a).__dict__['x'],接下來依次查找 type(a) 的上級(jí)基類,不包括元類。

但是,如果找到的值是定義了某個(gè)描述器方法的對(duì)象,則 Python 可能會(huì)重載默認(rèn)行為并轉(zhuǎn)而發(fā)起調(diào)用描述器方法。這具體發(fā)生在優(yōu)先級(jí)鏈的哪個(gè)環(huán)節(jié)則要根據(jù)所定義的描述器方法及其被調(diào)用的方式來決定。

描述器發(fā)起調(diào)用的開始點(diǎn)是一個(gè)綁定 a.x。參數(shù)的組合方式依 a 而定:

直接調(diào)用

最簡單但最不常見的調(diào)用方式是用戶代碼直接發(fā)起調(diào)用一個(gè)描述器方法: x.__get__(a)

實(shí)例綁定

如果綁定到一個(gè)對(duì)象實(shí)例,a.x 會(huì)被轉(zhuǎn)換為調(diào)用: type(a).__dict__['x'].__get__(a, type(a))。

類綁定

如果綁定到一個(gè)類,A.x 會(huì)被轉(zhuǎn)換為調(diào)用: A.__dict__['x'].__get__(None, A)。

超綁定

如果 asuper 的一個(gè)實(shí)例,則綁定 super(B, obj).m() 會(huì)在 obj.__class__.__mro__ 中搜索 B 的直接上級(jí)基類 A 然后通過以下調(diào)用發(fā)起調(diào)用描述器: A.__dict__['m'].__get__(obj, obj.__class__)。

對(duì)于實(shí)例綁定,發(fā)起描述器調(diào)用的優(yōu)先級(jí)取決于定義了哪些描述器方法。一個(gè)描述器可以定義 __get__()__set__()__delete__() 的任意組合。如果它沒有定義 __get__(),則訪問屬性會(huì)返回描述器對(duì)象自身,除非對(duì)象的實(shí)例字典中有相應(yīng)屬性值。如果描述器定義了 __set__() 和/或 __delete__(),則它是一個(gè)數(shù)據(jù)描述器;如果以上兩個(gè)都未定義,則它是一個(gè)非數(shù)據(jù)描述器。通常,數(shù)據(jù)描述器會(huì)同時(shí)定義 __get__()__set__(),而非數(shù)據(jù)描述器只有 __get__() 方法。定義了 __set__()__get__() 的數(shù)據(jù)描述器總是會(huì)重載實(shí)例字典中的定義。與之相對(duì)的,非數(shù)據(jù)描述器可被實(shí)例所重載。

Python 方法 (包括 staticmethod()classmethod()) 都是作為非數(shù)據(jù)描述器來實(shí)現(xiàn)的。 因此實(shí)例可以重定義并重載方法。 這允許單個(gè)實(shí)例獲得與相同類的其他實(shí)例不一樣的行為。

property() 函數(shù)是作為數(shù)據(jù)描述器來實(shí)現(xiàn)的。因此實(shí)例不能重載特性屬性的行為。

3.3.2.4. __slots__?

__slots__ 允許我們顯式地聲明數(shù)據(jù)成員(例如特征屬性)并禁止創(chuàng)建 __dict____weakref__ (除非是在 __slots__ 中顯式地聲明或是在父類中可用。)

相比使用 __dict__ 此方式可以顯著地節(jié)省空間。 屬性查找速度也可得到顯著的提升。

object.__slots__?

這個(gè)類變量可賦值為字符串、可迭代對(duì)象或由實(shí)例使用的變量名構(gòu)成的字符串序列。 __slots__ 會(huì)為已聲明的變量保留空間,并阻止自動(dòng)為每個(gè)實(shí)例創(chuàng)建 __dict____weakref__。

3.3.2.4.1. 使用 __slots__ 的注意事項(xiàng)?
  • 當(dāng)繼承自一個(gè)未定義 __slots__ 的類時(shí),實(shí)例的 __dict____weakref__ 屬性將總是可訪問。

  • 沒有 __dict__ 變量,實(shí)例就不能給未在 __slots__ 定義中列出的新變量賦值。嘗試給一個(gè)未列出的變量名賦值將引發(fā) AttributeError。新變量需要?jiǎng)討B(tài)賦值,就要將 '__dict__' 加入到 __slots__ 聲明的字符串序列中。

  • 如果未給每個(gè)實(shí)例設(shè)置 __weakref__ 變量,定義了 __slots__ 的類就不支持對(duì)其實(shí)際的弱引用。如果需要弱引用支持,就要將 '__weakref__' 加入到 __slots__ 聲明的字符串序列中。

  • __slots__ 是通過為每個(gè)變量名創(chuàng)建描述器 (實(shí)現(xiàn)描述器) 在類層級(jí)上實(shí)現(xiàn)的。因此,類屬性不能被用來為通過 __slots__ 定義的實(shí)例變量設(shè)置默認(rèn)值;否則,類屬性就會(huì)覆蓋描述器賦值。

  • __slots__ 聲明的作用不只限于定義它的類。在父類中聲明的 __slots__ 在其子類中同樣可用。不過,子類將會(huì)獲得 __dict____weakref__ 除非它們也定義了 __slots__ (其中應(yīng)該僅包含對(duì)任何 額外 名稱的聲明位置)。

  • 如果一個(gè)類定義的位置在某個(gè)基類中也有定義,則由基類位置定義的實(shí)例變量將不可訪問(除非通過直接從基類獲取其描述器的方式)。這會(huì)使得程序的含義變成未定義。未來可能會(huì)添加一個(gè)防止此情況的檢查。

  • 非空的 __slots__ 不適用于派生自“可變長度”內(nèi)置類型例如 int、bytestuple 的派生類。

  • 任何非字符串可迭代對(duì)象都可以被賦值給 __slots__。映射也可以被使用;不過,未來可能會(huì)分別賦給每個(gè)鍵具有特殊含義的值。

  • __class__ 賦值僅在兩個(gè)類具有相同的 __slots__ 時(shí)才會(huì)起作用。

  • 帶有多個(gè)父類聲明位置的多重繼承也是可用的,但僅允許一個(gè)父類具有由聲明位置創(chuàng)建的屬性(其他基類必須具有空的位置布局) —— 違反規(guī)則將引發(fā) TypeError。

  • 如果為 __slots__ 使用了一個(gè)迭代器,則會(huì)為迭代器的每個(gè)值創(chuàng)建描述器。 但是 __slots__ 屬性將為一個(gè)空迭代器。

3.3.3. 自定義類創(chuàng)建?

當(dāng)一個(gè)類繼承其他類時(shí),那個(gè)類的 __init_subclass__ 會(huì)被調(diào)用。這樣就可以編寫能夠改變子類行為的類。這與類裝飾器有緊密的關(guān)聯(lián),但是類裝飾器是影響它們所應(yīng)用的特定類,而 __init_subclass__ 則只作用于定義了該方法的類所派生的子類。

classmethod object.__init_subclass__(cls)?

當(dāng)所在類派生子類時(shí)此方法就會(huì)被調(diào)用。cls 將指向新的子類。如果定義為一個(gè)普通實(shí)例方法,此方法將被隱式地轉(zhuǎn)換為類方法。

傳入一個(gè)新類的關(guān)鍵字參數(shù)會(huì)被傳給父類的 __init_subclass__。為了與其他使用 __init_subclass__ 的類兼容,應(yīng)當(dāng)根據(jù)需要去掉部分關(guān)鍵字參數(shù)再將其余的傳給基類,例如:

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__ 的默認(rèn)實(shí)現(xiàn)什么都不做,只在帶任意參數(shù)調(diào)用時(shí)引發(fā)一個(gè)錯(cuò)誤。

注解

元類提示 metaclass 將被其它類型機(jī)制消耗掉,并不會(huì)被傳給 __init_subclass__ 的實(shí)現(xiàn)。實(shí)際的元類(而非顯式的提示)可通過 type(cls) 訪問。

3.6 新版功能.

3.3.3.1. 元類?

默認(rèn)情況下,類是使用 type() 來構(gòu)建的。類體會(huì)在一個(gè)新的命名空間內(nèi)執(zhí)行,類名會(huì)被局部綁定到 type(name, bases, namespace) 的結(jié)果。

類創(chuàng)建過程可通過在定義行傳入 metaclass 關(guān)鍵字參數(shù),或是通過繼承一個(gè)包含此參數(shù)的現(xiàn)有類來進(jìn)行定制。在以下示例中,MyClassMySubclass 都是 Meta 的實(shí)例:

class Meta(type):
    pass

class MyClass(metaclass=Meta):
    pass

class MySubclass(MyClass):
    pass

在類定義內(nèi)指定的任何其他關(guān)鍵字參數(shù)都會(huì)在下面所描述的所有元類操作中進(jìn)行傳遞。

當(dāng)一個(gè)類定義被執(zhí)行時(shí),將發(fā)生以下步驟:

  • 解析 MRO 條目;

  • 確定適當(dāng)?shù)脑悾?/p>

  • 準(zhǔn)備類命名空間;

  • 執(zhí)行類主體;

  • 創(chuàng)建類對(duì)象。

3.3.3.2. 解析 MRO 條目?

如果在類定義中出現(xiàn)的基類不是 type 的實(shí)例,則使用 __mro_entries__ 方法對(duì)其進(jìn)行搜索,當(dāng)找到結(jié)果時(shí),它會(huì)以原始基類元組做參數(shù)進(jìn)行調(diào)用。此方法必須返回類的元組以替代此基類被使用。元組可以為空,在此情況下原始基類將被忽略。

參見

PEP 560 - 對(duì) typing 模塊和泛型類型的核心支持

3.3.3.3. 確定適當(dāng)?shù)脑?a class="headerlink" href="#determining-the-appropriate-metaclass" title="永久鏈接至標(biāo)題">?

為一個(gè)類定義確定適當(dāng)?shù)脑愂歉鶕?jù)以下規(guī)則:

  • 如果沒有基類且沒有顯式指定元類,則使用 type();

  • 如果給出一個(gè)顯式元類而且 不是 type() 的實(shí)例,則其會(huì)被直接用作元類;

  • 如果給出一個(gè) type() 的實(shí)例作為顯式元類,或是定義了基類,則使用最近派生的元類。

最近派生的元類會(huì)從顯式指定的元類(如果有)以及所有指定的基類的元類(即 type(cls))中選取。最近派生的元類應(yīng)為 所有 這些候選元類的一個(gè)子類型。如果沒有一個(gè)候選元類符合該條件,則類定義將失敗并拋出 TypeError。

3.3.3.4. 準(zhǔn)備類命名空間?

一旦確定了適當(dāng)?shù)脑?,則將準(zhǔn)備好類命名空間。 如果元類具有 __prepare__ 屬性,它會(huì)以 namespace = metaclass.__prepare__(name, bases, **kwds) 的形式被調(diào)用(其中如果有任何額外的關(guān)鍵字參數(shù),則應(yīng)當(dāng)來自類定義)。 __prepare__ 方法應(yīng)該被實(shí)現(xiàn)為 classmethod()。 __prepare__ 所返回的命名空間會(huì)被傳入 __new__,但是當(dāng)最終的類對(duì)象被創(chuàng)建時(shí),該命名空間會(huì)被拷貝到一個(gè)新的 dict 中。

如果元類沒有 __prepare__ 屬性,則類命名空間將初始化為一個(gè)空的有序映射。

參見

PEP 3115 - Python 3000 中的元類

引入 __prepare__ 命名空間鉤子

3.3.3.5. 執(zhí)行類主體?

類主體會(huì)以(類似于) exec(body, globals(), namespace) 的形式被執(zhí)行。普通調(diào)用與 exec() 的關(guān)鍵區(qū)別在于當(dāng)類定義發(fā)生于函數(shù)內(nèi)部時(shí),詞法作用域允許類主體(包括任何方法)引用來自當(dāng)前和外部作用域的名稱。

但是,即使當(dāng)類定義發(fā)生于函數(shù)內(nèi)部時(shí),在類內(nèi)部定義的方法仍然無法看到在類作用域?qū)哟紊隙x的名稱。類變量必須通過實(shí)例的第一個(gè)形參或類方法來訪問,或者是通過下一節(jié)中描述的隱式詞法作用域的 __class__ 引用。

3.3.3.6. 創(chuàng)建類對(duì)象?

一旦執(zhí)行類主體完成填充類命名空間,將通過調(diào)用 metaclass(name, bases, namespace, **kwds) 創(chuàng)建類對(duì)象(此處的附加關(guān)鍵字參數(shù)與傳入 __prepare__ 的相同)。

如果類主體中有任何方法引用了 __class__super,這個(gè)類對(duì)象會(huì)通過零參數(shù)形式的 super(). __class__ 所引用,這是由編譯器所創(chuàng)建的隱式閉包引用。這使用零參數(shù)形式的 super() 能夠正確標(biāo)識(shí)正在基于詞法作用域來定義的類,而被用于進(jìn)行當(dāng)前調(diào)用的類或?qū)嵗齽t是基于傳遞給方法的第一個(gè)參數(shù)來標(biāo)識(shí)的。

CPython implementation detail: 在 CPython 3.6 及之后的版本中,__class__ 單元會(huì)被作為類命名空間中的 __classcell__ 一項(xiàng)傳遞給元類。如果存在,這必須被向上傳播給 type.__new__ 調(diào)用,以便能正確地初始化該類。如果不這樣做,在 Python 3.6 中將導(dǎo)致 DeprecationWarning,而在 Python 3.8 中將引發(fā) RuntimeError。

當(dāng)使用默認(rèn)的元類 type 或者任何最終會(huì)調(diào)用 type.__new__ 的元類時(shí),以下額外的自定義步驟將在創(chuàng)建類對(duì)象之后被發(fā)起調(diào)用:

  • 首先,type.__new__ 將收集類命名空間中所有定義了 __set_name__() 方法的描述器;

  • 接下來,所有這些 __set_name__ 方法將使用所定義的類和特定描述器所賦的名稱進(jìn)行調(diào)用;

  • 最后,將在新類根據(jù)方法解析順序所確定的直接父類上調(diào)用 __init_subclass__() 鉤子。

在類對(duì)象創(chuàng)建之后,它會(huì)被傳給包含在類定義中的類裝飾器(如果有的話),得到的對(duì)象將作為已定義的類綁定到局部命名空間。

當(dāng)通過 type.__new__ 創(chuàng)建一個(gè)新類時(shí),提供以作為命名空間形參的對(duì)象會(huì)被復(fù)制到一個(gè)新的有序映射并丟棄原對(duì)象。這個(gè)新副本包裝于一個(gè)只讀代理中,后者則成為類對(duì)象的 __dict__ 屬性。

參見

PEP 3135 - 新的超類型

描述隱式的 __class__ 閉包引用

3.3.3.7. 元類的作用?

元類的潛在作用非常廣泛。已經(jīng)過嘗試的設(shè)想包括枚舉、日志、接口檢查、自動(dòng)委托、自動(dòng)特征屬性創(chuàng)建、代理、框架以及自動(dòng)資源鎖定/同步等等。

3.3.4. 自定義實(shí)例及子類檢查?

以下方法被用來重載 isinstance()issubclass() 內(nèi)置函數(shù)的默認(rèn)行為。

特別地,元類 abc.ABCMeta 實(shí)現(xiàn)了這些方法以便允許將抽象基類(ABC)作為“虛擬基類”添加到任何類或類型(包括內(nèi)置類型),包括其他 ABC 之中。

class.__instancecheck__(self, instance)?

如果 instance 應(yīng)被視為 class 的一個(gè)(直接或間接)實(shí)例則返回真值。如果定義了此方法,則會(huì)被調(diào)用以實(shí)現(xiàn) isinstance(instance, class)。

class.__subclasscheck__(self, subclass)?

Return true 如果 subclass 應(yīng)被視為 class 的一個(gè)(直接或間接)子類則返回真值。如果定義了此方法,則會(huì)被調(diào)用以實(shí)現(xiàn) issubclass(subclass, class)。

請(qǐng)注意這些方法的查找是基于類的類型(元類)。它們不能作為類方法在實(shí)際的類中被定義。這與基于實(shí)例被調(diào)用的特殊方法的查找是一致的,只有在此情況下實(shí)例本身被當(dāng)作是類。

參見

PEP 3119 - 引入抽象基類

新增功能描述,通過 __instancecheck__()__subclasscheck__() 來定制 isinstance()issubclass() 行為,加入此功能的動(dòng)機(jī)是出于向該語言添加抽象基類的內(nèi)容(參見 abc 模塊)。

3.3.5. 模擬泛型類型?

通過定義一個(gè)特殊方法,可以實(shí)現(xiàn)由 PEP 484 所規(guī)定的泛型類語法 (例如 List[int]):

classmethod object.__class_getitem__(cls, key)?

按照 key 參數(shù)指定的類型返回一個(gè)表示泛型類的專門化對(duì)象。

此方法的查找會(huì)基于對(duì)象自身,并且當(dāng)定義于類體內(nèi)部時(shí),此方法將隱式地成為類方法。請(qǐng)注意,此機(jī)制主要是被保留用于靜態(tài)類型提示,不鼓勵(lì)在其他場合使用。

參見

PEP 560 - 對(duì) typing 模塊和泛型類型的核心支持

3.3.6. 模擬可調(diào)用對(duì)象?

object.__call__(self[, args...])?

此方法會(huì)在實(shí)例作為一個(gè)函數(shù)被“調(diào)用”時(shí)被調(diào)用;如果定義了此方法,則 x(arg1, arg2, ...) 就相當(dāng)于 x.__call__(arg1, arg2, ...) 的快捷方式。

3.3.7. 模擬容器類型?

可以定義下列方法來實(shí)現(xiàn)容器對(duì)象。 容器通常屬于序列(如列表或元組)或映射(如字典),但也存在其他形式的容器。 前幾個(gè)方法集被用于模擬序列或是模擬映射;兩者的不同之處在于序列允許的鍵應(yīng)為整數(shù) k0 <= k < N 其中 N 是序列或定義指定區(qū)間的項(xiàng)的切片對(duì)象的長度。 此外還建議讓映射提供 keys(), values(), items(), get(), clear(), setdefault(), pop(), popitem(), copy() 以及 update() 等方法,它們的行為應(yīng)與 Python 標(biāo)準(zhǔn)字典對(duì)象的相應(yīng)方法類似。 此外 collections.abc 模塊提供了一個(gè) MutableMapping 抽象基類以便根據(jù)由 __getitem__(), __setitem__(), __delitem__(), 和 keys() 組成的基本集來創(chuàng)建所需的方法。 可變序列還應(yīng)像 Python 標(biāo)準(zhǔn)列表對(duì)象那樣提供 append(), count(), index(), extend(), insert(), pop(), remove(), reverse()sort() 等方法。 最后,序列類型還應(yīng)通過定義下文描述的 __add__(), __radd__(), __iadd__(), __mul__(), __rmul__()__imul__() 等方法來實(shí)現(xiàn)加法(指拼接)和乘法(指重復(fù));它們不應(yīng)定義其他數(shù)值運(yùn)算符。 此外還建議映射和序列都實(shí)現(xiàn) __contains__() 方法以允許高效地使用 in 運(yùn)算符;對(duì)于映射,in 應(yīng)該搜索映射的鍵;對(duì)于序列,則應(yīng)搜索其中的值。 另外還建議映射和序列都實(shí)現(xiàn) __iter__() 方法以允許高效地迭代容器中的條目;對(duì)于映射,__iter__() 應(yīng)當(dāng)?shù)鷮?duì)象的鍵;對(duì)于序列,則應(yīng)當(dāng)?shù)渲械闹怠?/p>

object.__len__(self)?

調(diào)用此方法以實(shí)現(xiàn)內(nèi)置函數(shù) len()。應(yīng)該返回對(duì)象的長度,以一個(gè) >= 0 的整數(shù)表示。此外,如果一個(gè)對(duì)象未定義 __bool__() 方法而其 __len__() 方法返回值為零,則在布爾運(yùn)算中會(huì)被視為假值。

CPython implementation detail: 在 CPython 中,要求長度最大為 sys.maxsize。如果長度大于 sys.maxsize 則某些特性 (例如 len()) 可能會(huì)引發(fā) OverflowError。要通過真值檢測來防止引發(fā) OverflowError,對(duì)象必須定義 __bool__() 方法。

object.__length_hint__(self)?

調(diào)用此方法以實(shí)現(xiàn) operator.length_hint()。 應(yīng)該返回對(duì)象長度的估計(jì)值(可能大于或小于實(shí)際長度)。 此長度應(yīng)為一個(gè) >= 0 的整數(shù)。 返回值也可以為 NotImplemented,這會(huì)被視作與 __length_hint__ 方法完全不存在時(shí)一樣處理。 此方法純粹是為了優(yōu)化性能,并不要求正確無誤。

3.4 新版功能.

注解

切片是通過下述三個(gè)專門方法完成的。以下形式的調(diào)用

a[1:2] = b

會(huì)為轉(zhuǎn)寫為

a[slice(1, 2, None)] = b

其他形式以此類推。略去的切片項(xiàng)總是以 None 補(bǔ)全。

object.__getitem__(self, key)?

調(diào)用此方法以實(shí)現(xiàn) self[key] 的求值。對(duì)于序列類型,接受的鍵應(yīng)為整數(shù)和切片對(duì)象。請(qǐng)注意負(fù)數(shù)索引(如果類想要模擬序列類型)的特殊解讀是取決于 __getitem__() 方法。如果 key 的類型不正確則會(huì)引發(fā) TypeError 異常;如果為序列索引集范圍以外的值(在進(jìn)行任何負(fù)數(shù)索引的特殊解讀之后)則應(yīng)引發(fā) IndexError 異常。對(duì)于映射類型,如果 key 找不到(不在容器中)則應(yīng)引發(fā) KeyError 異常。

注解

for 循環(huán)在有不合法索引時(shí)會(huì)期待捕獲 IndexError 以便正確地檢測到序列的結(jié)束。

object.__setitem__(self, key, value)?

調(diào)用此方法以實(shí)現(xiàn)向 self[key] 賦值。注意事項(xiàng)與 __getitem__() 相同。為對(duì)象實(shí)現(xiàn)此方法應(yīng)該僅限于需要映射允許基于鍵修改值或添加鍵,或是序列允許元素被替換時(shí)。不正確的 key 值所引發(fā)的異常應(yīng)與 __getitem__() 方法的情況相同。

object.__delitem__(self, key)?

調(diào)用此方法以實(shí)現(xiàn) self[key] 的刪除。注意事項(xiàng)與 __getitem__() 相同。為對(duì)象實(shí)現(xiàn)此方法應(yīng)該權(quán)限于需要映射允許移除鍵,或是序列允許移除元素時(shí)。不正確的 key 值所引發(fā)的異常應(yīng)與 __getitem__() 方法的情況相同。

object.__missing__(self, key)?

此方法由 dict.__getitem__() 在找不到字典中的鍵時(shí)調(diào)用以實(shí)現(xiàn) dict 子類的 self[key]

object.__iter__(self)?

此方法在需要為容器創(chuàng)建迭代器時(shí)被調(diào)用。此方法應(yīng)該返回一個(gè)新的迭代器對(duì)象,它能夠逐個(gè)迭代容器中的所有對(duì)象。對(duì)于映射,它應(yīng)該逐個(gè)迭代容器中的鍵。

迭代器對(duì)象也需要實(shí)現(xiàn)此方法;它們需要返回對(duì)象自身。有關(guān)迭代器對(duì)象的詳情請(qǐng)參看 迭代器類型 一節(jié)。

object.__reversed__(self)?

此方法(如果存在)會(huì)被 reversed() 內(nèi)置函數(shù)調(diào)用以實(shí)現(xiàn)逆向迭代。它應(yīng)當(dāng)返回一個(gè)新的以逆序逐個(gè)迭代容器內(nèi)所有對(duì)象的迭代器對(duì)象。

如果未提供 __reversed__() 方法,則 reversed() 內(nèi)置函數(shù)將回退到使用序列協(xié)議 (__len__()__getitem__())。支持序列協(xié)議的對(duì)象應(yīng)當(dāng)僅在能夠提供比 reversed() 所提供的實(shí)現(xiàn)更高效的實(shí)現(xiàn)時(shí)才提供 __reversed__() 方法。

成員檢測運(yùn)算符 (innot in) 通常以對(duì)容器進(jìn)行逐個(gè)迭代的方式來實(shí)現(xiàn)。 不過,容器對(duì)象可以提供以下特殊方法并采用更有效率的實(shí)現(xiàn),這樣也不要求對(duì)象必須為可迭代對(duì)象。

object.__contains__(self, item)?

調(diào)用此方法以實(shí)現(xiàn)成員檢測運(yùn)算符。如果 itemself 的成員則應(yīng)返回真,否則返回假。對(duì)于映射類型,此檢測應(yīng)基于映射的鍵而不是值或者鍵值對(duì)。

對(duì)于未定義 __contains__() 的對(duì)象,成員檢測將首先嘗試通過 __iter__() 進(jìn)行迭代,然后再使用 __getitem__() 的舊式序列迭代協(xié)議,參看 語言參考中的相應(yīng)部分。

3.3.8. 模擬數(shù)字類型?

定義以下方法即可模擬數(shù)字類型。特定種類的數(shù)字不支持的運(yùn)算(例如非整數(shù)不能進(jìn)行位運(yùn)算)所對(duì)應(yīng)的方法應(yīng)當(dāng)保持未定義狀態(tài)。

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)?

調(diào)用這些方法來實(shí)現(xiàn)二進(jìn)制算術(shù)運(yùn)算 (+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |)。例如,求表達(dá)式 x + y 的值,其中 x 是具有 __add__() 方法的類的一個(gè)實(shí)例,則會(huì)調(diào)用 x.__add__(y)。__divmod__() 方法應(yīng)該等價(jià)于使用 __floordiv__()__mod__(),它不應(yīng)該被關(guān)聯(lián)到 __truediv__()。請(qǐng)注意如果要支持三元版本的內(nèi)置 pow() 函數(shù),則 __pow__() 的定義應(yīng)該接受可選的第三個(gè)參數(shù)。

如果這些方法中的某一個(gè)不支持與所提供參數(shù)進(jìn)行運(yùn)算,它應(yīng)該返回 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)?

調(diào)用這些方法來實(shí)現(xiàn)具有反射(交換)操作數(shù)的二進(jìn)制算術(shù)運(yùn)算 (+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |)。這些成員函數(shù)僅會(huì)在左操作數(shù)不支持相應(yīng)運(yùn)算 3 且兩個(gè)操作數(shù)類型不同時(shí)被調(diào)用。4 例如,求表達(dá)式 x - y 的值,其中 y 是具有 __rsub__() 方法的類的一個(gè)實(shí)例,則當(dāng) x.__sub__(y) 返回 NotImplemented 時(shí)會(huì)調(diào)用 y.__rsub__(x)。

請(qǐng)注意三元版的 pow() 并不會(huì)嘗試調(diào)用 __rpow__() (因?yàn)閺?qiáng)制轉(zhuǎn)換規(guī)則會(huì)太過復(fù)雜)。

注解

如果右操作數(shù)類型為左操作數(shù)類型的一個(gè)子類,且該子類提供了指定運(yùn)算的反射方法,則此方法會(huì)先于左操作數(shù)的非反射方法被調(diào)用。此行為可允許子類重載其祖先類的運(yùn)算符。

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)?

調(diào)用這些方法來實(shí)現(xiàn)擴(kuò)展算術(shù)賦值 (+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=)。這些方法應(yīng)該嘗試進(jìn)行自身操作 (修改 self) 并返回結(jié)果 (結(jié)果應(yīng)該但并非必須為 self)。如果某個(gè)方法未被定義,相應(yīng)的擴(kuò)展算術(shù)賦值將回退到普通方法。例如,如果 x 是具有 __iadd__() 方法的類的一個(gè)實(shí)例,則 x += y 就等價(jià)于 x = x.__iadd__(y)。否則就如 x + y 的求值一樣選擇 x.__add__(y)y.__radd__(x)。在某些情況下,擴(kuò)展賦值可導(dǎo)致未預(yù)期的錯(cuò)誤 (參見 為什么 a_tuple[i] += ['item'] 會(huì)在執(zhí)行加法時(shí)引發(fā)異常?),但此行為實(shí)際上是數(shù)據(jù)模型的一個(gè)組成部分。

object.__neg__(self)?
object.__pos__(self)?
object.__abs__(self)?
object.__invert__(self)?

調(diào)用此方法以實(shí)現(xiàn)一元算術(shù)運(yùn)算 (-, +, abs()~)。

object.__complex__(self)?
object.__int__(self)?
object.__float__(self)?

調(diào)用這些方法以實(shí)現(xiàn)內(nèi)置函數(shù) complex(), int()float()。應(yīng)當(dāng)返回一個(gè)相應(yīng)類型的值。

object.__index__(self)?

調(diào)用此方法以實(shí)現(xiàn) operator.index() 以及 Python 需要無損地將數(shù)字對(duì)象轉(zhuǎn)換為整數(shù)對(duì)象的場合(例如切片或是內(nèi)置的 bin(), hex()oct() 函數(shù))。 存在此方法表明數(shù)字對(duì)象屬于整數(shù)類型。 必須返回一個(gè)整數(shù)。

注解

為了具有一致的整數(shù)類型類,當(dāng)定義了 __index__() 的時(shí)候也應(yīng)當(dāng)定義 __int__(),兩者應(yīng)當(dāng)返回相同的值。

object.__round__(self[, ndigits])?
object.__trunc__(self)?
object.__floor__(self)?
object.__ceil__(self)?

調(diào)用這些方法以實(shí)現(xiàn)內(nèi)置函數(shù) round() 以及 math 函數(shù) trunc(), floor()ceil()。 除了將 ndigits 傳給 __round__() 的情況之外這些方法的返回值都應(yīng)當(dāng)是原對(duì)象截?cái)酁?Integral (通常為 int)。

如果未定義 __int__() 則內(nèi)置函數(shù) int() 會(huì)回退到 __trunc__()。

3.3.9. with 語句上下文管理器?

上下文管理器 是一個(gè)對(duì)象,它定義了在執(zhí)行 with 語句時(shí)要建立的運(yùn)行時(shí)上下文。 上下文管理器處理進(jìn)入和退出所需運(yùn)行時(shí)上下文以執(zhí)行代碼塊。 通常使用 with 語句(在 with 語句 中描述),但是也可以通過直接調(diào)用它們的方法來使用。

上下文管理器的典型用法包括保存和恢復(fù)各種全局狀態(tài),鎖定和解鎖資源,關(guān)閉打開的文件等等。

要了解上下文管理器的更多信息,請(qǐng)參閱 上下文管理器類型。

object.__enter__(self)?

進(jìn)入與此對(duì)象相關(guān)的運(yùn)行時(shí)上下文。 with 語句將會(huì)綁定這個(gè)方法的返回值到 as 子句中指定的目標(biāo),如果有的話。

object.__exit__(self, exc_type, exc_value, traceback)?

退出關(guān)聯(lián)到此對(duì)象的運(yùn)行時(shí)上下文。 各個(gè)參數(shù)描述了導(dǎo)致上下文退出的異常。 如果上下文是無異常地退出的,三個(gè)參數(shù)都將為 None

如果提供了異常,并且希望方法屏蔽此異常(即避免其被傳播),則應(yīng)當(dāng)返回真值。 否則的話,異常將在退出此方法時(shí)按正常流程處理。

請(qǐng)注意 __exit__() 方法不應(yīng)該重新引發(fā)被傳入的異常,這是調(diào)用者的責(zé)任。

參見

PEP 343 - "with" 語句

Python with 語句的規(guī)范描述、背景和示例。

3.3.10. 特殊方法查找?

對(duì)于自定義類來說,特殊方法的隱式發(fā)起調(diào)用僅保證在其定義于對(duì)象類型中時(shí)能正確地發(fā)揮作用,而不能定義在對(duì)象實(shí)例字典中。 該行為就是以下代碼會(huì)引發(fā)異常的原因。:

>>> 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()

此行為背后的原理在于包括類型對(duì)象在內(nèi)的所有對(duì)象都會(huì)實(shí)現(xiàn)的幾個(gè)特殊方法,例如 __hash__()__repr__()。 如果這些方法的隱式查找使用了傳統(tǒng)的查找過程,它們會(huì)在對(duì)類型對(duì)象本身發(fā)起調(diào)用時(shí)失敗:

>>> 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

以這種方式不正確地嘗試發(fā)起調(diào)用一個(gè)類的未綁定方法有時(shí)被稱為‘元類混淆’,可以通過在查找特殊方法時(shí)繞過實(shí)例的方式來避免:

>>> type(1).__hash__(1) == hash(1)
True
>>> type(int).__hash__(int) == hash(int)
True

除了為了正確性而繞過任何實(shí)例屬性之外,隱式特殊方法查找通常也會(huì)繞過 __getattribute__() 方法,甚至包括對(duì)象的元類:

>>> 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__() 機(jī)制為解析器內(nèi)部的速度優(yōu)化提供了顯著的空間,其代價(jià)則是犧牲了處理特殊方法時(shí)的一些靈活性(特殊方法 必須 設(shè)置在類對(duì)象本身上以便始終一致地由解釋器發(fā)起調(diào)用)。

3.4. 協(xié)程?

3.4.1. 可等待對(duì)象?

awaitable 對(duì)象主要實(shí)現(xiàn)了 __await__() 方法。 從 async def 函數(shù)返回的 Coroutine 對(duì)象即屬于可等待對(duì)象。

注解

從帶有 types.coroutine()asyncio.coroutine() 裝飾器的生成器返回的 generator iterator 對(duì)象也屬于可等待對(duì)象,但它們并未實(shí)現(xiàn) __await__()。

object.__await__(self)?

必須返回一個(gè) iterator。 應(yīng)當(dāng)被用來實(shí)現(xiàn) awaitable 對(duì)象。 例如,asyncio.Future 實(shí)現(xiàn)了此方法以與 await 表達(dá)式相兼容。

3.5 新版功能.

參見

PEP 492 了解有關(guān)可等待對(duì)象的詳細(xì)信息。

3.4.2. 協(xié)程對(duì)象?

Coroutine 對(duì)象屬于 awaitable 對(duì)象。 協(xié)程的執(zhí)行可通過調(diào)用 __await__() 并迭代其結(jié)果來進(jìn)行控制。 當(dāng)協(xié)程結(jié)束執(zhí)行并返回時(shí),迭代器會(huì)引發(fā) StopIteration,該異常的 value 屬性將指向返回值。 如果協(xié)程引發(fā)了異常,它會(huì)被迭代器所傳播。 協(xié)程不應(yīng)該直接引發(fā)未處理的 StopIteration 異常。

協(xié)程也具有下面列出的方法,它們類似于生成器的對(duì)應(yīng)方法 (參見 生成器-迭代器的方法)。 但是,與生成器不同,協(xié)程并不直接支持迭代。

在 3.5.2 版更改: 等待一個(gè)協(xié)程超過一次將引發(fā) RuntimeError。

coroutine.send(value)?

開始或恢復(fù)協(xié)程的執(zhí)行。 如果 valueNone,則這相當(dāng)于前往 __await__() 所返回迭代器的下一項(xiàng)。 如果 value 不為 None,此方法將委托給導(dǎo)致協(xié)程掛起的迭代器的 send() 方法。 其結(jié)果(返回值,StopIteration 或是其他異常)將與上述對(duì) __await__() 返回值進(jìn)行迭代的結(jié)果相同。

coroutine.throw(type[, value[, traceback]])?

在協(xié)程內(nèi)引發(fā)指定的異常。 此方法將委托給導(dǎo)致協(xié)程掛起的迭代器的 throw() 方法,如果存在該方法。 否則的話,異常會(huì)在掛起點(diǎn)被引發(fā)。 其結(jié)果(返回值,StopIteration 或是其他異常)將與上述對(duì) __await__() 返回值進(jìn)行迭代的結(jié)果相同。 如果異常未在協(xié)程內(nèi)被捕獲,則將回傳給調(diào)用者。

coroutine.close()?

此方法會(huì)使得協(xié)程清理自身并退出。 如果協(xié)程被掛起,此方法會(huì)先委托給導(dǎo)致協(xié)程掛起的迭代器的 close() 方法,如果存在該方法。 然后它會(huì)在掛起點(diǎn)引發(fā) GeneratorExit,使得協(xié)程立即清理自身。 最后,協(xié)程會(huì)被標(biāo)記為已結(jié)束執(zhí)行,即使它根本未被啟動(dòng)。

當(dāng)協(xié)程對(duì)象將要被銷毀時(shí),會(huì)使用以上處理過程來自動(dòng)關(guān)閉。

3.4.3. 異步迭代器?

異步迭代器 可以在其 __anext__ 方法中調(diào)用異步代碼。

異步迭代器可在 async for 語句中使用。

object.__aiter__(self)?

必須返回一個(gè) 異步迭代器 對(duì)象。

object.__anext__(self)?

必須返回一個(gè) 可迭代對(duì)象 輸出迭代器的下一結(jié)果值。 當(dāng)?shù)Y(jié)束時(shí)應(yīng)該引發(fā) StopAsyncIteration 錯(cuò)誤。

異步可迭代對(duì)象的一個(gè)示例:

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__ 可以返回一個(gè) 可迭代對(duì)象 并解析為 異步迭代器。

從 Python 3.7 開始,__aiter__ 必須 返回一個(gè)異步迭代器對(duì)象。 返回任何其他對(duì)象都將導(dǎo)致 TypeError 錯(cuò)誤。

3.4.4. 異步上下文管理器?

異步上下文管理器上下文管理器 的一種,它能夠在其 __aenter____aexit__ 方法中暫停執(zhí)行。

異步上下文管理器可在 async with 語句中使用。

object.__aenter__(self)?

此方法在語義上類似于 __enter__(),僅有的區(qū)別是它必須返回一個(gè) 可等待對(duì)象。

object.__aexit__(self, exc_type, exc_value, traceback)?

此方法在語義上類似于 __exit__(),僅有的區(qū)別是它必須返回一個(gè) 可等待對(duì)象

異步上下文管理器類的一個(gè)示例:

class AsyncContextManager:
    async def __aenter__(self):
        await log('entering context')

    async def __aexit__(self, exc_type, exc, tb):
        await log('exiting context')

3.5 新版功能.

備注

1

在某些情況下 有可能 基于可控的條件改變一個(gè)對(duì)象的類型。 但這通常不是個(gè)好主意,因?yàn)槿绻幚聿划?dāng)會(huì)導(dǎo)致一些非常怪異的行為。

2

__hash__(), __iter__(), __reversed__() 以及 __contains__() 方法對(duì)此有特殊處理;其他方法仍會(huì)引發(fā) TypeError,但可能依靠 None 屬于不可調(diào)用對(duì)象的行為來做到這一點(diǎn)。

3

這里的“不支持”是指該類無此方法,或方法返回 NotImplemented。 如果你想強(qiáng)制回退到右操作數(shù)的反射方法,請(qǐng)不要設(shè)置方法為 None — 那會(huì)造成顯式地 阻塞 此種回退的相反效果。

4

對(duì)于相同類型的操作數(shù),如果非反射方法 (例如 __add__()) 失敗則會(huì)認(rèn)為相應(yīng)運(yùn)算不被支持,這就是反射方法未被調(diào)用的原因。