3. 數(shù)據(jù)模型?
3.1. 對象、值與類型?
對象 是 Python 中對數(shù)據(jù)的抽象。 Python 程序中的所有數(shù)據(jù)都是由對象或?qū)ο箝g關(guān)系來表示的。 (從某種意義上說,按照馮·諾依曼的“存儲程序計算機”模型,代碼本身也是由對象來表示的。)
每個對象都有各自的編號、類型和值。一個對象被創(chuàng)建后,它的 編號 就絕不會改變;你可以將其理解為該對象在內(nèi)存中的地址。 'is' 運算符可以比較兩個對象的編號是否相同;id() 函數(shù)能返回一個代表其編號的整型數(shù)。
CPython implementation detail: 在 CPython 中,id(x) 就是存放 x 的內(nèi)存的地址。
對象的類型決定該對象所支持的操作 (例如 "對象是否有長度屬性?") 并且定義了該類型的對象可能的取值。type() 函數(shù)能返回一個對象的類型 (類型本身也是對象)。與編號一樣,一個對象的 類型 也是不可改變的。1
有些對象的 值 可以改變。值可以改變的對象被稱為 可變的;值不可以改變的對象就被稱為 不可變的。(一個不可變?nèi)萜鲗ο笕绻瑢勺儗ο蟮囊茫?dāng)后者的值改變時,前者的值也會改變;但是該容器仍屬于不可變對象,因為它所包含的對象集是不會改變的。因此,不可變并不嚴格等同于值不能改變,實際含義要更微妙。) 一個對象的可變性是由其類型決定的;例如,數(shù)字、字符串和元組是不可變的,而字典和列表是可變的。
對象絕不會被顯式地銷毀;然而,當(dāng)無法訪問時它們可能會被作為垃圾回收。允許具體的實現(xiàn)推遲垃圾回收或完全省略此機制 --- 如何實現(xiàn)垃圾回收是實現(xiàn)的質(zhì)量問題,只要可訪問的對象不會被回收即可。
CPython implementation detail: CPython 目前使用帶有 (可選) 延遲檢測循環(huán)鏈接垃圾的引用計數(shù)方案,會在對象不可訪問時立即回收其中的大部分,但不保證回收包含循環(huán)引用的垃圾。請查看 gc 模塊的文檔了解如何控制循環(huán)垃圾的收集相關(guān)信息。其他實現(xiàn)會有不同的行為方式,CPython 現(xiàn)有方式也可能改變。不要依賴不可訪問對象的立即終結(jié)機制 (所以你應(yīng)當(dāng)總是顯式地關(guān)閉文件)。
注意:使用實現(xiàn)的跟蹤或調(diào)試功能可能令正常情況下會被回收的對象繼續(xù)存活。還要注意通過 'try...except' 語句捕捉異常也可能令對象保持存活。
有些對象包含對 "外部" 資源的引用,例如打開文件或窗口。當(dāng)對象被作為垃圾回收時這些資源也應(yīng)該會被釋放,但由于垃圾回收并不確保發(fā)生,這些對象還提供了明確地釋放外部資源的操作,通常為一個 close() 方法。強烈推薦在程序中顯式關(guān)閉此類對象。'try...finally' 語句和 'with' 語句提供了進行此種操作的更便捷方式。
有些對象包含對其他對象的引用;它們被稱為 容器。容器的例子有元組、列表和字典等。這些引用是容器對象值的組成部分。在多數(shù)情況下,當(dāng)談?wù)撘粋€容器的值時,我們是指所包含對象的值而不是其編號;但是,當(dāng)我們談?wù)撘粋€容器的可變性時,則僅指其直接包含的對象的編號。因此,如果一個不可變?nèi)萜?(例如元組) 包含對一個可變對象的引用,則當(dāng)該可變對象被改變時容器的值也會改變。
類型會影響對象行為的幾乎所有方面。甚至對象編號的重要性也在某種程度上受到影響: 對于不可變類型,會得出新值的運算實際上會返回對相同類型和取值的任一現(xiàn)有對象的引用,而對于可變類型來說這是不允許的。例如在 a = 1; b = 1 之后,a 和 b 可能會也可能不會指向同一個值為一的對象,這取決于具體實現(xiàn),但是在 c = []; d = [] 之后,c 和 d 保證會指向兩個不同、單獨的新建空列表。(請注意 c = d = [] 則是將同一個對象賦值給 c 和 d。)
3.2. 標(biāo)準類型層級結(jié)構(gòu)?
以下是 Python 內(nèi)置類型的列表。擴展模塊 (具體實現(xiàn)會以 C, Java 或其他語言編寫) 可以定義更多的類型。未來版本的 Python 可能會加入更多的類型 (例如有理數(shù)、高效存儲的整型數(shù)組等等),不過新增類型往往都是通過標(biāo)準庫來提供的。
以下部分類型的描述中包含有 '特殊屬性列表' 段落。這些屬性提供對具體實現(xiàn)的訪問而非通常使用。它們的定義在未來可能會改變。
- None
此類型只有一種取值。是一個具有此值的單獨對象。此對象通過內(nèi)置名稱
None訪問。在許多情況下它被用來表示空值,例如未顯式指明返回值的函數(shù)將返回 None。它的邏輯值為假。- NotImplemented
此類型只有一種取值。是一個具有此值的單獨對象。此對象通過內(nèi)置名稱
NotImplemented訪問。數(shù)值方法和豐富比較方法如未實現(xiàn)指定運算符表示的運算則應(yīng)返回此值。(解釋器會根據(jù)指定運算符繼續(xù)嘗試反向運算或其他回退操作)。它的邏輯值為真。詳情參見 實現(xiàn)算術(shù)運算。
- Ellipsis
此類型只有一種取值。是一個具有此值的單獨對象。此對象通過字面值
...或內(nèi)置名稱Ellipsis訪問。它的邏輯值為真。numbers.Number此類對象由數(shù)字字面值創(chuàng)建,并會被作為算術(shù)運算符和算術(shù)內(nèi)置函數(shù)的返回結(jié)果。數(shù)字對象是不可變的;一旦創(chuàng)建其值就不再改變。Python 中的數(shù)字當(dāng)然非常類似數(shù)學(xué)中的數(shù)字,但也受限于計算機中的數(shù)字表示方法。
Python 區(qū)分整型數(shù)、浮點型數(shù)和復(fù)數(shù):
numbers.Integral此類對象表示數(shù)學(xué)中整數(shù)集合的成員 (包括正數(shù)和負數(shù))。
整型數(shù)可細分為兩種類型:
整型 (
int)此類對象表示任意大小的數(shù)字,僅受限于可用的內(nèi)存 (包括虛擬內(nèi)存)。在變換和掩碼運算中會以二進制表示,負數(shù)會以 2 的補碼表示,看起來像是符號位向左延伸補滿空位。
- 布爾型 (
bool) 此類對象表示邏輯值 False 和 True。代表
False和True值的兩個對象是唯二的布爾對象。布爾類型是整型的子類型,兩個布爾值在各種場合的行為分別類似于數(shù)值 0 和 1,例外情況只有在轉(zhuǎn)換為字符串時分別返回字符串"False"或"True"。
整型數(shù)表示規(guī)則的目的是在涉及負整型數(shù)的變換和掩碼運算時提供最為合理的解釋。
- 布爾型 (
numbers.Real(float)此類對象表示機器級的雙精度浮點數(shù)。其所接受的取值范圍和溢出處理將受制于底層的機器架構(gòu) (以及 C 或 Java 實現(xiàn))。Python 不支持單精度浮點數(shù);支持后者通常的理由是節(jié)省處理器和內(nèi)存消耗,但這點節(jié)省相對于在 Python 中使用對象的開銷來說太過微不足道,因此沒有理由包含兩種浮點數(shù)而令該語言變得復(fù)雜。
numbers.Complex(complex)此類對象以一對機器級的雙精度浮點數(shù)來表示復(fù)數(shù)值。有關(guān)浮點數(shù)的附帶規(guī)則對其同樣有效。一個復(fù)數(shù)值
z的實部和虛部可通過只讀屬性z.real和z.imag來獲取。
- 序列
此類對象表示以非負整數(shù)作為索引的有限有序集。內(nèi)置函數(shù)
len()可返回一個序列的條目數(shù)量。當(dāng)一個序列的長度為 n 時,索引集包含數(shù)字 0, 1, ..., n-1。序列 a 的條目 i 可通過a[i]選擇。序列還支持切片:
a[i:j]選擇索引號為 k 的所有條目,i<=k<j。當(dāng)用作表達式時,序列的切片就是一個與序列類型相同的新序列。新序列的索引還是從 0 開始。有些序列還支持帶有第三個 "step" 形參的 "擴展切片":
a[i:j:k]選擇 a 中索引號為 x 的所有條目,x = i + n*k, n>=0且 i<=x<j。序列可根據(jù)其可變性來加以區(qū)分:
- 不可變序列
不可變序列類型的對象一旦創(chuàng)建就不能再改變。(如果對象包含對其他對象的引用,其中的可變對象就是可以改變的;但是,一個不可變對象所直接引用的對象集是不能改變的。)
以下類型屬于不可變對象:
- 字符串
字符串是由 Unicode 碼位值組成的序列。范圍在
U+0000 - U+10FFFF之內(nèi)的所有碼位值都可在字符串中使用。Python 沒有char類型;而是將字符串中的每個碼位表示為一個長度為1的字符串對象。內(nèi)置函數(shù)ord()可將一個碼位由字符串形式轉(zhuǎn)換成一個范圍在0 - 10FFFF之內(nèi)的整型數(shù);chr()可將一個范圍在0 - 10FFFF之內(nèi)的整型數(shù)轉(zhuǎn)換為長度為1的對應(yīng)字符串對象。str.encode()可以使用指定的文本編碼將str轉(zhuǎn)換為bytes,而bytes.decode()則可以實現(xiàn)反向的解碼。- 元組
一個元組中的條目可以是任意 Python 對象。包含兩個或以上條目的元組由逗號分隔的表達式構(gòu)成。只有一個條目的元組 ('單項元組') 可通過在表達式后加一個逗號來構(gòu)成 (一個表達式本身不能創(chuàng)建為元組,因為圓括號要用來設(shè)置表達式分組)。一個空元組可通過一對內(nèi)容為空的圓括號創(chuàng)建。
- 字節(jié)串
字節(jié)串對象是不可變的數(shù)組。其中每個條目都是一個 8 位字節(jié),以取值范圍 0 <= x < 256 的整型數(shù)表示。字節(jié)串字面值 (例如
b'abc') 和內(nèi)置的bytes()構(gòu)造器可被用來創(chuàng)建字節(jié)串對象。字節(jié)串對象還可以通過decode()方法解碼為字符串。
- 可變序列
可變序列在被創(chuàng)建后仍可被改變。下標(biāo)和切片標(biāo)注可被用作賦值和
del(刪除) 語句的目標(biāo)。目前有兩種內(nèi)生可變序列類型:
- 列表
列表中的條目可以是任意 Python 對象。列表由用方括號括起并由逗號分隔的多個表達式構(gòu)成。(注意創(chuàng)建長度為 0 或 1 的列表無需使用特殊規(guī)則。)
- 字節(jié)數(shù)組
字節(jié)數(shù)組對象屬于可變數(shù)組。可以通過內(nèi)置的
bytearray()構(gòu)造器來創(chuàng)建。除了是可變的 (因而也是不可哈希的),在其他方面字節(jié)數(shù)組提供的接口和功能都與不可變的bytes對象一致。
擴展模塊
array提供了一個額外的可變序列類型示例,collections模塊也是如此。
- 集合類型
此類對象表示由不重復(fù)且不可變對象組成的無序且有限的集合。因此它們不能通過下標(biāo)來索引。但是它們可被迭代,也可用內(nèi)置函數(shù)
len()返回集合中的條目數(shù)。集合常見的用處是快速成員檢測,去除序列中的重復(fù)項,以及進行交、并、差和對稱差等數(shù)學(xué)運算。對于集合元素所采用的不可變規(guī)則與字典的鍵相同。注意數(shù)字類型遵循正常的數(shù)字比較規(guī)則: 如果兩個數(shù)字相等 (例如
1和1.0),則同一集合中只能包含其中一個。目前有兩種內(nèi)生集合類型:
- 集合
此類對象表示可變集合。它們可通過內(nèi)置的
set()構(gòu)造器創(chuàng)建,并且創(chuàng)建之后可以通過方法進行修改,例如add()。- 凍結(jié)集合
此類對象表示不可變集合。它們可通過內(nèi)置的
frozenset()構(gòu)造器創(chuàng)建。由于 frozenset 對象不可變且 hashable,它可以被用作另一個集合的元素或是字典的鍵。
- 映射
此類對象表示由任意索引集合所索引的對象的集合。通過下標(biāo)
a[k]可在映射a中選擇索引為k的條目;這可以在表達式中使用,也可作為賦值或del語句的目標(biāo)。內(nèi)置函數(shù)len()可返回一個映射中的條目數(shù)。目前只有一種內(nèi)生映射類型:
- 字典
此類對象表示由幾乎任意值作為索引的有限個對象的集合。不可作為鍵的值類型只有包含列表或字典或其他可變類型,通過值而非對象編號進行比較的值,其原因在于高效的字典實現(xiàn)需要使用鍵的哈希值以保持一致性。用作鍵的數(shù)字類型遵循正常的數(shù)字比較規(guī)則: 如果兩個數(shù)字相等 (例如
1和1.0) 則它們均可來用來索引同一個字典條目。字典會保留插入順序,這意味著鍵將以它們被添加的順序在字典中依次產(chǎn)生。 替換某個現(xiàn)有的鍵不會改變其順序,但是移除某個鍵再重新插入則會將其添加到末尾而不會保留其原有位置。
字典是可變的;它們可通過
{...}標(biāo)注來創(chuàng)建 (參見 字典顯示 小節(jié))。擴展模塊
dbm.ndbm和dbm.gnu提供了額外的映射類型示例,collections模塊也是如此。在 3.7 版更改: 在 Python 3.6 版之前字典不會保留插入順序。 在 CPython 3.6 中插入順序會被保留,但這在當(dāng)時被當(dāng)作是一個實現(xiàn)細節(jié)而非確定的語言特性。
- 可調(diào)用類型
此類型可以被應(yīng)用于函數(shù)調(diào)用操作 (參見 調(diào)用 小節(jié)):
- 用戶定義函數(shù)
用戶定義函數(shù)對象可通過函數(shù)定義來創(chuàng)建 (參見 函數(shù)定義 小節(jié))。它被調(diào)用時應(yīng)附帶一個參數(shù)列表,其中包含的條目應(yīng)與函數(shù)所定義的形參列表一致。
特殊屬性:
屬性
含義
__doc__該函數(shù)的文檔字符串,沒有則為
None;不會被子類繼承。可寫
該函數(shù)的名稱。
可寫
該函數(shù)的 qualified name。
3.3 新版功能.
可寫
__module__該函數(shù)所屬模塊的名稱,沒有則為
None。可寫
__defaults__由具有默認值的參數(shù)的默認參數(shù)值組成的元組,如無任何參數(shù)具有默認值則為
None。可寫
__code__表示編譯后的函數(shù)體的代碼對象。
可寫
__globals__對存放該函數(shù)中全局變量的字典的引用 --- 函數(shù)所屬模塊的全局命名空間。
只讀
命名空間支持的函數(shù)屬性。
可寫
__closure__None或包含該函數(shù)可用變量的綁定的單元的元組。有關(guān)cell_contents屬性的詳情見下。只讀
__annotations__包含參數(shù)標(biāo)注的字典。字典的鍵是參數(shù)名,如存在返回標(biāo)注則為
'return'。可寫
__kwdefaults__僅包含關(guān)鍵字參數(shù)默認值的字典。
可寫
大部分標(biāo)有 "Writable" 的屬性均會檢查賦值的類型。
函數(shù)對象也支持獲取和設(shè)置任意屬性,例如這可以被用來給函數(shù)附加元數(shù)據(jù)。使用正規(guī)的屬性點號標(biāo)注獲取和設(shè)置此類屬性。注意當(dāng)前實現(xiàn)僅支持用戶定義函數(shù)屬性。未來可能會增加支持內(nèi)置函數(shù)屬性。
單元對象具有
cell_contents屬性。這可被用來獲取以及設(shè)置單元的值。有關(guān)函數(shù)定義的額外信息可以從其代碼對象中提取;參見下文對內(nèi)部類型的描述。
- 實例方法
實例方法用于結(jié)合類、類實例和任何可調(diào)用對象 (通常為用戶定義函數(shù))。
特殊的只讀屬性:
__self__為類實例對象本身,__func__為函數(shù)對象;__doc__為方法的文檔 (與__func__.__doc__作用相同);__name__為方法名稱 (與__func__.__name__作用相同);__module__為方法所屬模塊的名稱,沒有則為None。方法還支持獲取 (但不能設(shè)置) 下層函數(shù)對象的任意函數(shù)屬性。
用戶定義方法對象可在獲取一個類的屬性時被創(chuàng)建 (也可能通過該類的一個實例),如果該屬性為用戶定義函數(shù)對象或類方法對象。
當(dāng)通過從類實例獲取一個用戶定義函數(shù)對象的方式創(chuàng)建一個實例方法對象時,類實例對象的
__self__屬性即為該實例,并會綁定方法對象。該新建方法的__func__屬性就是原來的函數(shù)對象。當(dāng)通過從類或?qū)嵗@取另一個方法對象的方式創(chuàng)建一個用戶定義方法對象時,其行為將等同于一個函數(shù)對象,例外的只有新實例的
__func__屬性將不是原來的方法對象,而是其__func__屬性。當(dāng)通過從類或?qū)嵗@取一個類方法對象的方式創(chuàng)建一個實例對象時,實例對象的
__self__屬性為該類本身,其__func__屬性為類方法對應(yīng)的下層函數(shù)對象。當(dāng)一個實例方法對象被調(diào)用時,會調(diào)用對應(yīng)的下層函數(shù) (
__func__),并將類實例 (__self__) 插入?yún)?shù)列表的開頭。例如,當(dāng)C是一個包含了f()函數(shù)定義的類,而x是C的一個實例,則調(diào)用x.f(1)就等同于調(diào)用C.f(x, 1)。當(dāng)一個實例方法對象是衍生自一個類方法對象時,保存在
__self__中的 "類實例" 實際上會是該類本身,因此無論是調(diào)用x.f(1)還是C.f(1)都等同于調(diào)用f(C,1),其中f為對應(yīng)的下層函數(shù)。請注意從函數(shù)對象到實例方法對象的變換會在每一次從實例獲取屬性時發(fā)生。在某些情況下,一種高效的優(yōu)化方式是將屬性賦值給一個本地變量并調(diào)用該本地變量。還要注意這樣的變換只發(fā)生于用戶定義函數(shù);其他可調(diào)用對象 (以及所有不可調(diào)用對象) 在被獲取時都不會發(fā)生變換。還有一個需要關(guān)注的要點是作為一個類實例屬性的用戶定義函數(shù)不會被轉(zhuǎn)換為綁定方法;這樣的變換 僅當(dāng) 函數(shù)是類屬性時才會發(fā)生。
- 生成器函數(shù)
一個使用
yield語句 (見 yield 語句 章節(jié))的函數(shù)或方法被稱作一個 生成器函數(shù)。 這樣的函數(shù)在被調(diào)用時,總是返回一個可以執(zhí)行函數(shù)體的迭代器對象:調(diào)用該迭代器的iterator.__next__()方法將會導(dǎo)致這個函數(shù)一直運行直到它使用yield語句提供了一個值為止。 當(dāng)這個函數(shù)執(zhí)行return語句或者執(zhí)行到末尾時,將引發(fā)StopIteration異常并且這個迭代器將到達所返回的值集合的末尾。- 協(xié)程函數(shù)
使用
async def來定義的函數(shù)或方法就被稱為 協(xié)程函數(shù)。這樣的函數(shù)在被調(diào)用時會返回一個 coroutine 對象。它可能包含await表達式以及async with和async for語句。詳情可參見 協(xié)程對象 一節(jié)。- 異步生成器函數(shù)
使用
async def來定義并包含yield語句的函數(shù)或方法就被稱為 異步生成器函數(shù)。這樣的函數(shù)在被調(diào)用時會返回一個異步迭代器對象,該對象可在async for語句中用來執(zhí)行函數(shù)體。調(diào)用異步迭代器的
aiterator.__anext__()方法將會返回一個 awaitable,此對象會在被等待時執(zhí)行直到使用yield表達式輸出一個值。當(dāng)函數(shù)執(zhí)行時到空的return語句或是最后一條語句時,將會引發(fā)StopAsyncIteration異常,異步迭代器也會到達要輸出的值集合的末尾。- 內(nèi)置函數(shù)
內(nèi)置函數(shù)對象是對于 C 函數(shù)的外部封裝。內(nèi)置函數(shù)的例子包括
len()和math.sin()(math是一個標(biāo)準內(nèi)置模塊)。內(nèi)置函數(shù)參數(shù)的數(shù)量和類型由 C 函數(shù)決定。特殊的只讀屬性:__doc__是函數(shù)的文檔字符串,如果沒有則為None;__name__是函數(shù)的名稱;__self__設(shè)定為None(參見下一條目);__module__是函數(shù)所屬模塊的名稱,如果沒有則為None。- 內(nèi)置方法
此類型實際上是內(nèi)置函數(shù)的另一種形式,只不過還包含了一個傳入 C 函數(shù)的對象作為隱式的額外參數(shù)。內(nèi)置方法的一個例子是
alist.append(),其中 alist 為一個列表對象。在此示例中,特殊的只讀屬性__self__會被設(shè)為 alist 所標(biāo)記的對象。- 類
類是可調(diào)用的。此種對象通常是作為“工廠”來創(chuàng)建自身的實例,類也可以有重載
__new__()的變體類型。調(diào)用的參數(shù)會傳給__new__(),而且通常也會傳給__init__()來初始化新的實例。- 類實例
任意類的實例通過在所屬類中定義
__call__()方法即能成為可調(diào)用的對象。
- 模塊
模塊是 Python 代碼的基本組織單元,由 導(dǎo)入系統(tǒng) 創(chuàng)建,由
import語句發(fā)起調(diào)用,或者通過importlib.import_module()和內(nèi)置的__import__()等函數(shù)發(fā)起調(diào)用。 模塊對象具有由字典對象實現(xiàn)的命名空間(這是被模塊中定義的函數(shù)的__globals__屬性引用的字典)。 屬性引用被轉(zhuǎn)換為該字典中的查找,例如m.x相當(dāng)于m.__dict__["x"]。 模塊對象不包含用于初始化模塊的代碼對象(因為初始化完成后不需要它)。屬性賦值會更新模塊的命名空間字典,例如
m.x = 1等同于m.__dict__["x"] = 1。預(yù)定義的 (可寫) 屬性:
__name__為模塊的名稱;__doc__為模塊的文檔字符串,如果沒有則為None;__annotations__(可選) 為一個包含 變量標(biāo)注 的字典,它是在模塊體執(zhí)行時獲取的;__file__是模塊對應(yīng)的被加載文件的路徑名,如果它是加載自一個文件的話。某些類型的模塊可能沒有__file__屬性,例如 C 模塊是靜態(tài)鏈接到解釋器內(nèi)部的; 對于從一個共享庫動態(tài)加載的擴展模塊來說該屬性為該共享庫文件的路徑名。特殊的只讀屬性:
__dict__為以字典對象表示的模塊命名空間。CPython implementation detail: 由于 CPython 清理模塊字典的設(shè)定,當(dāng)模塊離開作用域時模塊字典將會被清理,即使該字典還有活動的引用。想避免此問題,可復(fù)制該字典或保持模塊狀態(tài)以直接使用其字典。
- 自定義類
自定義類這種類型一般通過類定義來創(chuàng)建 (參見 類定義 一節(jié))。每個類都有通過一個字典對象實現(xiàn)的獨立命名空間。類屬性引用會被轉(zhuǎn)化為在此字典中查找,例如
C.x會被轉(zhuǎn)化為C.__dict__["x"](不過也存在一些鉤子對象以允許其他定位屬性的方式)。當(dāng)未在其中發(fā)現(xiàn)某個屬性名稱時,會繼續(xù)在基類中查找。這種基類查找使用 C3 方法解析順序,即使存在 '鉆石形' 繼承結(jié)構(gòu)即有多條繼承路徑連到一個共同祖先也能保持正確的行為。有關(guān) Python 使用的 C3 MRO 的詳情可查看配合 2.3 版發(fā)布的文檔 https://www.python.org/download/releases/2.3/mro/.當(dāng)一個類屬性引用 (假設(shè)類名為
C) 會產(chǎn)生一個類方法對象時,它將轉(zhuǎn)化為一個__self__屬性為C的實例方法對象。當(dāng)其會產(chǎn)生一個靜態(tài)方法對象時,它將轉(zhuǎn)化為該靜態(tài)方法對象所封裝的對象。從類的__dict__所包含內(nèi)容以外獲取屬性的其他方式請參看 實現(xiàn)描述器 一節(jié)。類屬性賦值會更新類的字典,但不會更新基類的字典。
類對象可被調(diào)用 (見上文) 以產(chǎn)生一個類實例 (見下文)。
特殊屬性:
__name__為類的名稱;__module__為類所在模塊的名稱;__dict__為包含類命名空間的字典;__bases__為包含基類的元組,按其在基類列表中的出現(xiàn)順序排列;__doc__為類的文檔字符串,如果沒有則為None;__annotations__(可選) 為一個包含 變量標(biāo)注 的字典,它是在類體執(zhí)行時獲取的。- 類實例
類實例可通過調(diào)用類對象來創(chuàng)建 (見上文)。每個類實例都有通過一個字典對象實現(xiàn)的獨立命名空間,屬性引用會首先在此字典中查找。當(dāng)未在其中發(fā)現(xiàn)某個屬性,而實例對應(yīng)的類中有該屬性時,會繼續(xù)在類屬性中查找。如果找到的類屬性為一個用戶定義函數(shù)對象,它會被轉(zhuǎn)化為實例方法對象,其
__self__屬性即該實例。靜態(tài)方法和類方法對象也會被轉(zhuǎn)化;參見上文 "Classes" 一節(jié)。要了解其他通過類實例來獲取相應(yīng)類屬性的方式可參見 實現(xiàn)描述器 一節(jié),這樣得到的屬性可能與實際存放于類的__dict__中的對象不同。如果未找到類屬性,而對象對應(yīng)的類具有__getattr__()方法,則會調(diào)用該方法來滿足查找要求。屬性賦值和刪除會更新實例的字典,但不會更新對應(yīng)類的字典。如果類具有
__setattr__()或__delattr__()方法,則將調(diào)用方法而不再直接更新實例的字典。如果類實例具有某些特殊名稱的方法,就可以偽裝為數(shù)字、序列或映射。參見 特殊方法名稱 一節(jié)。
- I/O 對象 (或稱文件對象)
file object 表示一個打開的文件。有多種快捷方式可用來創(chuàng)建文件對象:
open()內(nèi)置函數(shù),以及os.popen(),os.fdopen()和 socket 對象的makefile()方法 (還可能使用某些擴展模塊所提供的其他函數(shù)或方法)。sys.stdin,sys.stdout和sys.stderr會初始化為對應(yīng)于解釋器標(biāo)準輸入、輸出和錯誤流的文件對象;它們都會以文本模式打開,因此都遵循io.TextIOBase抽象類所定義的接口。- 內(nèi)部類型
某些由解釋器內(nèi)部使用的類型也被暴露給用戶。它們的定義可能隨未來解釋器版本的更新而變化,為內(nèi)容完整起見在此處一并介紹。
- 代碼對象
代碼對象表示 編譯為字節(jié)的 可執(zhí)行 Python 代碼,或稱 bytecode。代碼對象和函數(shù)對象的區(qū)別在于函數(shù)對象包含對函數(shù)全局對象 (函數(shù)所屬的模塊) 的顯式引用,而代碼對象不包含上下文;而且默認參數(shù)值會存放于函數(shù)對象而不是代碼對象內(nèi) (因為它們表示在運行時算出的值)。與函數(shù)對象不同,代碼對象不可變,也不包含對可變對象的引用 (不論是直接還是間接)。
特殊的只讀屬性:
co_name為函數(shù)名稱;co_argcount為位置參數(shù)的數(shù)量 (包括有默認值的參數(shù));co_nlocals為函數(shù)使用的局部變量數(shù)量 (包括參數(shù));co_varnames為一個包括局部變量名稱的元組 (以參數(shù)名打頭);co_cellvars為一個包含被嵌套函數(shù)所引用的局部變量名稱的元組;co_freevars為一個包含自由變量名稱的元組;co_code為一個表示字節(jié)碼指令序列的字符串;co_consts為一個包含字節(jié)碼所使用的字面值的元組;co_names為一個包含字節(jié)碼所使用的名稱的元組;co_filename為被編碼代碼所在的文件名;co_firstlineno為函數(shù)首行的行號;co_lnotab為一個以編碼表示的從字節(jié)碼偏移量到行號的映射的字符串 (詳情參見解釋器的源碼);co_stacksize為要求的棧大小;co_flags為一個以整數(shù)編碼表示的多個解釋器所用的旗標(biāo)。以下是可用于
co_flags的標(biāo)志位定義:如果函數(shù)使用*arguments語法來接受任意數(shù)量的位置參數(shù),則0x04位被設(shè)置;如果函數(shù)使用**keywords語法來接受任意數(shù)量的關(guān)鍵字參數(shù),則0x08位被設(shè)置;如果函數(shù)是一個生成器,則0x20位被設(shè)置。未來特性聲明 (
from __future__ import division) 也使用co_flags中的標(biāo)志位來指明代碼對象的編譯是否啟用特定的特性: 如果函數(shù)編譯時啟用未來除法特性則設(shè)置0x2000位; 在更早的 Python 版本中則使用0x10和0x1000位。co_flags中的其他位被保留為內(nèi)部使用。如果代碼對象表示一個函數(shù),
co_consts中的第一項將是函數(shù)的文檔字符串,如果未定義則為None。
- 幀對象
幀對象表示執(zhí)行幀。它們可能出現(xiàn)在回溯對象中 (見下文),還會被傳遞給注冊跟蹤函數(shù)。
特殊的只讀屬性:
f_back為前一堆棧幀 (指向調(diào)用者),如是最底層堆棧幀則為None;f_code為此幀中所執(zhí)行的代碼對象;f_locals為用于查找本地變量的字典;f_globals則用于查找全局變量;f_builtins用于查找內(nèi)置 (固有) 名稱;f_lasti給出精確指令 (這是代碼對象的字節(jié)碼字符串的一個索引)。特殊的可寫屬性:
f_trace,如果不為None,則是在代碼執(zhí)行期間調(diào)用各類事件的函數(shù) (由調(diào)試器使用)。通常每個新源碼行會觸發(fā)一個事件 - 這可以通過將f_trace_lines設(shè)為False來禁用。具體的實現(xiàn) 可能 會通過將
f_trace_opcodes設(shè)為True來允許按操作碼請求事件。請注意如果跟蹤函數(shù)引發(fā)的異常逃逸到被跟蹤的函數(shù)中,這可能會導(dǎo)致未定義的解釋器行為。f_lineno為幀的當(dāng)前行號 --- 在這里寫入從一個跟蹤函數(shù)內(nèi)部跳轉(zhuǎn)的指定行 (僅用于最底層的幀)。調(diào)試器可以通過寫入 f_lineno 實現(xiàn)一個 Jump 命令 (即設(shè)置下一語句)。幀對象支持一個方法:
-
frame.clear()? 此方法清除該幀持有的全部對本地變量的引用。而且如果該幀屬于一個生成器,生成器會被完成。這有助于打破包含幀對象的循環(huán)引用 (例如當(dāng)捕獲一個異常并保存其回溯在之后使用)。
如果該幀當(dāng)前正在執(zhí)行則會引發(fā)
RuntimeError。3.4 新版功能.
-
- 回溯對象
回溯對象表示一個異常的棧跟蹤記錄。當(dāng)異常發(fā)生時會隱式地創(chuàng)建一個回溯對象,也可能通過調(diào)用
types.TracebackType顯式地創(chuàng)建。對于隱式地創(chuàng)建的回溯對象,當(dāng)查找異常句柄使得執(zhí)行棧展開時,會在每個展開層級的當(dāng)前回溯之前插入一個回溯對象。當(dāng)進入一個異常句柄時,棧跟蹤將對程序啟用。(參見 try 語句 一節(jié)。) 它可作為
sys.exc_info()所返回的元組的第三項,以及所捕獲異常的__traceback__屬性被獲取。當(dāng)程序不包含可用的句柄時,棧跟蹤會 (以良好的格式) 寫入標(biāo)準錯誤流;如果解釋器處于交互模式,它也可作為
sys.last_traceback對用戶啟用。對于顯式創(chuàng)建的回溯對象,則由回溯對象的創(chuàng)建者來決定應(yīng)該如何鏈接
tb_next屬性來構(gòu)成完整的棧跟蹤。特殊的只讀屬性:
tb_frame指向當(dāng)前層級的執(zhí)行幀;tb_lineno給出發(fā)生異常所在的行號;tb_lasti標(biāo)示具體指令。如果異常發(fā)生于沒有匹配的 except 子句或有 finally 子句的try語句中,回溯對象中的行號和最后指令可能與相應(yīng)幀對象中行號不同。特殊的可寫屬性:
tb_next為棧跟蹤中的下一層級 (通往發(fā)生異常的幀),如果沒有下一層級則為None。在 3.7 版更改: 回溯對象現(xiàn)在可以使用 Python 代碼顯式地實例化,現(xiàn)有實例的
tb_next屬性可以被更新。- 切片對象
切片對象用來表示
__getitem__()方法用到的切片。 該對象也可使用內(nèi)置的slice()函數(shù)來創(chuàng)建。特殊的只讀屬性:
start為下界;stop為上界;step為步長值; 各值如省略則為None。這些屬性可具有任意類型。切片對象支持一個方法:
-
slice.indices(self, length)? 此方法接受一個整型參數(shù) length 并計算在切片對象被應(yīng)用到 length 指定長度的條目序列時切片的相關(guān)信息應(yīng)如何描述。 其返回值為三個整型數(shù)組成的元組;這些數(shù)分別為切片的 start 和 stop 索引號以及 step 步長值。索引號缺失或越界則按照與正規(guī)切片相一致的方式處理。
-
- 靜態(tài)方法對象
靜態(tài)方法對象提供了一種避免上文所述將函數(shù)對象轉(zhuǎn)換為方法對象的方式。靜態(tài)方法對象為對任意其他對象的封裝,通常用來封裝用戶定義方法對象。當(dāng)從類或類實例獲取一個靜態(tài)方法對象時,實際返回的對象是封裝的對象,它不會被進一步轉(zhuǎn)換。靜態(tài)方法對象自身不是可調(diào)用的,但它們所封裝的對象通常都是可調(diào)用的。靜態(tài)方法對象可通過內(nèi)置的
staticmethod()構(gòu)造器來創(chuàng)建。- 類方法對象
類方法對象和靜態(tài)方法一樣是對其他對象的封裝,會改變從類或類實例獲取該對象的方式。類方法對象在此類獲取操作中的行為已在上文 "用戶定義方法" 一節(jié)中描述。類方法對象可通過內(nèi)置的
classmethod()構(gòu)造器來創(chuàng)建。
3.3. 特殊方法名稱?
一個類可以通過定義具有特殊名稱的方法來實現(xiàn)由特殊語法所引發(fā)的特定操作 (例如算術(shù)運算或下標(biāo)與切片)。這是 Python 實現(xiàn) 操作符重載 的方式,允許每個類自行定義基于操作符的特定行為。例如,如果一個類定義了名為 __getitem__() 的方法,并且 x 為該類的一個實例,則 x[i] 基本就等同于 type(x).__getitem__(x, i)。除非有說明例外情況,在沒有定義適當(dāng)方法的情況下嘗試執(zhí)行一種操作將引發(fā)一個異常 (通常為 AttributeError 或 TypeError)。
將一個特殊方法設(shè)為 None 表示對應(yīng)的操作不可用。例如,如果一個類將 __iter__() 設(shè)為 None,則該類就是不可迭代的,因此對其實例調(diào)用 iter() 將引發(fā)一個 TypeError (而不會回退至 __getitem__()). 2
在實現(xiàn)模擬任何內(nèi)置類型的類時,很重要的一點是模擬的實現(xiàn)程度對于被模擬對象來說應(yīng)當(dāng)是有意義的。例如,提取單個元素的操作對于某些序列來說是適宜的,但提取切片可能就沒有意義。(這種情況的一個實例是 W3C 的文檔對象模型中的 NodeList 接口。)
3.3.1. 基本定制?
-
object.__new__(cls[, ...])? 調(diào)用以創(chuàng)建一個 cls 類的新實例。
__new__()是一個靜態(tài)方法 (因為是特例所以你不需要顯式地聲明),它會將所請求實例所屬的類作為第一個參數(shù)。其余的參數(shù)會被傳遞給對象構(gòu)造器表達式 (對類的調(diào)用)。__new__()的返回值應(yīng)為新對象實例 (通常是 cls 的實例)。典型的實現(xiàn)會附帶適宜的參數(shù)使用
super().__new__(cls[, ...]),通過超類的__new__()方法來創(chuàng)建一個類的新實例,然后根據(jù)需要修改新創(chuàng)建的實例再將其返回。如果
__new__()返回一個 cls 的實例,則新實例的__init__()方法會在之后被執(zhí)行,例如__init__(self[, ...]),其中 self 為新實例,其余的參數(shù)與被傳遞給__new__()的相同。如果
__new__()未返回一個 cls 的實例,則新實例的__init__()方法就不會被執(zhí)行。__new__()的目的主要是允許不可變類型的子類 (例如 int, str 或 tuple) 定制實例創(chuàng)建過程。它也常會在自定義元類中被重載以便定制類創(chuàng)建過程。
-
object.__init__(self[, ...])? 在實例 (通過
__new__()) 被創(chuàng)建之后,返回調(diào)用者之前調(diào)用。其參數(shù)與傳遞給類構(gòu)造器表達式的參數(shù)相同。一個基類如果有__init__()方法,則其所派生的類如果也有__init__()方法,就必須顯式地調(diào)用它以確保實例基類部分的正確初始化;例如:super().__init__([args...]).因為對象是由
__new__()和__init__()協(xié)作構(gòu)造完成的 (由__new__()創(chuàng)建,并由__init__()定制),所以__init__()返回的值只能是None,否則會在運行時引發(fā)TypeError。
-
object.__del__(self)? 在實例將被銷毀時調(diào)用。 這還被稱為終結(jié)器或析構(gòu)器(不適當(dāng))。 如果一個基類具有
__del__()方法,則其所派生的類如果也有__del__()方法,就必須顯式地調(diào)用它以確保實例基類部分的正確清除。__del__()方法可以 (但不推薦!) 通過創(chuàng)建一個該實例的新引用來推遲其銷毀。這被稱為對象 重生。__del__()是否會在重生的對象將被銷毀時再次被調(diào)用是由具體實現(xiàn)決定的 ;當(dāng)前的 CPython 實現(xiàn)只會調(diào)用一次。當(dāng)解釋器退出時不會確保為仍然存在的對象調(diào)用
__del__()方法。注解
del x并不直接調(diào)用x.__del__()--- 前者會將x的引用計數(shù)減一,而后者僅會在x的引用計數(shù)變?yōu)榱銜r被調(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__()方法時周邊狀況已不確定,在其執(zhí)行期間發(fā)生的異常將被忽略,改為打印一個警告到sys.stderr。特別地:
-
object.__repr__(self)? 由
repr()內(nèi)置函數(shù)調(diào)用以輸出一個對象的“官方”字符串表示。如果可能,這應(yīng)類似一個有效的 Python 表達式,能被用來重建具有相同取值的對象(只要有適當(dāng)?shù)沫h(huán)境)。如果這不可能,則應(yīng)返回形式如<...some useful description...>的字符串。返回值必須是一個字符串對象。如果一個類定義了__repr__()但未定義__str__(),則在需要該類的實例的“非正式”字符串表示時也會使用__repr__()。此方法通常被用于調(diào)試,因此確保其表示的內(nèi)容包含豐富信息且無歧義是很重要的。
-
object.__str__(self)? 通過
str(object)以及內(nèi)置函數(shù)format()和print()調(diào)用以生成一個對象的“非正式”或格式良好的字符串表示。返回值必須為一個 字符串 對象。此方法與
object.__repr__()的不同點在于__str__()并不預(yù)期返回一個有效的 Python 表達式:可以使用更方便或更準確的描述信息。內(nèi)置類型
object所定義的默認實現(xiàn)會調(diào)用object.__repr__()。
-
object.__format__(self, format_spec)? 通過
format()內(nèi)置函數(shù)、擴展、格式化字符串字面值 的求值以及str.format()方法調(diào)用以生成一個對象的“格式化”字符串表示。 format_spec 參數(shù)為包含所需格式選項描述的字符串。 format_spec 參數(shù)的解讀是由實現(xiàn)__format__()的類型決定的,不過大多數(shù)類或是將格式化委托給某個內(nèi)置類型,或是使用相似的格式化選項語法。請參看 格式規(guī)格迷你語言 了解標(biāo)準格式化語法的描述。
返回值必須為一個字符串對象。
在 3.4 版更改:
object本身的 __format__ 方法如果被傳入任何非空字符,將會引發(fā)一個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ī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ù)對沒有相應(yīng)的實現(xiàn),富比較方法可能會返回單例對象
NotImplemented。按照慣例,成功的比較會返回False或True。不過實際上這些方法可以返回任意值,因此如果比較運算符是要用于布爾值判斷(例如作為if語句的條件),Python 會對返回值調(diào)用bool()以確定結(jié)果為真還是假。在默認情況下
__ne__()會委托給__eq__()并將結(jié)果取反,除非結(jié)果為NotImplemented。比較運算符之間沒有其他隱含關(guān)系,例如(x<y or x==y)為真并不意味著x<=y。要根據(jù)單根運算自動生成排序操作,請參看functools.total_ordering()。請查看
__hash__()的相關(guān)段落,了解創(chuàng)建可支持自定義比較運算并可用作字典鍵的 hashable 對象時要注意的一些事項。這些方法并沒有對調(diào)參數(shù)版本(在左邊參數(shù)不支持該操作但右邊參數(shù)支持時使用);而是
__lt__()和__gt__()互為對方的反射,__le__()和__ge__()互為對方的反射,而__eq__()和__ne__()則是它們自己的反射。如果兩個操作數(shù)的類型不同,且右操作數(shù)類型是左操作數(shù)類型的直接或間接子類,則優(yōu)先選擇右操作數(shù)的反射方法,否則優(yōu)先選擇左操作數(shù)的方法。虛擬子類不會被考慮。
-
object.__hash__(self)? 通過內(nèi)置函數(shù)
hash()調(diào)用以對哈希集的成員進行操作,屬于哈希集的類型包括set、frozenset以及dict。__hash__()應(yīng)該返回一個整數(shù)。對象比較結(jié)果相同所需的唯一特征屬性是其具有相同的哈希值;建議的做法是把參與比較的對象全部組件的哈希值混在一起,即將它們打包為一個元組并對該元組做哈希運算。例如:def __hash__(self): return hash((self.name, self.nick, self.color))
注解
hash()會從一個對象自定義的__hash__()方法返回值中截斷為Py_ssize_t的大小。通常對 64 位構(gòu)建為 8 字節(jié),對 32 位構(gòu)建為 4 字節(jié)。如果一個對象的__hash__()必須在不同位大小的構(gòu)建上進行互操作,請確保檢查全部所支持構(gòu)建的寬度。做到這一點的簡單方法是使用python -c "import sys; print(sys.hash_info.width)"。如果一個類沒有定義
__eq__()方法,那么也不應(yīng)該定義__hash__()操作;如果它定義了__eq__()但沒有定義__hash__(),則其實例將不可被用作可哈希集的項。如果一個類定義了可變對象并實現(xiàn)了__eq__()方法,則不應(yīng)該實現(xiàn)__hash__(),因為可哈希集的實現(xiàn)要求鍵的哈希集是不可變的(如果對象的哈希值發(fā)生改變,它將處于錯誤的哈希桶中)。用戶定義的類默認帶有
__eq__()和__hash__()方法;使用它們與任何對象(自己除外)比較必定不相等,并且x.__hash__()會返回一個恰當(dāng)?shù)闹狄源_保x == y同時意味著x is y且hash(x) == hash(y)。一個類如果重載了
__eq__()且沒有定義__hash__()則會將其__hash__()隱式地設(shè)為None。當(dāng)一個類的__hash__()方法為None時,該類的實例將在一個程序嘗試獲取其哈希值時正確地引發(fā)TypeError,并會在檢測isinstance(obj, collections.abc.Hashable)時被正確地識別為不可哈希對象。如果一個重載了
__eq__()的類需要保留來自父類的__hash__()實現(xiàn),則必須通過設(shè)置__hash__ = <ParentClass>.__hash__來顯式地告知解釋器。如果一個沒有重載
__eq__()的類需要去掉哈希支持,則應(yīng)該在類定義中包含__hash__ = None。一個自定義了__hash__()以顯式地引發(fā)TypeError的類會被isinstance(obj, collections.abc.Hashable)調(diào)用錯誤地識別為可哈希對象。注解
在默認情況下,str、bytes 和 datetime 對象的
__hash__()值會使用一個不可預(yù)知的隨機值“加鹽”。雖然它們會在一個單獨 Python 進程中保持不變,它們的哈希值在重復(fù)運行的 Python 之間是不可預(yù)測的。這種做法是為了防止以下形式的拒絕服務(wù)攻擊:通過仔細選擇輸入來利用字典插入操作在最壞情況下的執(zhí)行效率即 O(n^2) 復(fù)雜度。詳情見 http://www.ocert.org/advisories/ocert-2011-003.html
改變哈希值會影響集合的迭代次序。Python 也從不保證這個次序不會被改變(通常它在 32 位和 64 位構(gòu)建上是不一致的)。
另見
PYTHONHASHSEED.在 3.3 版更改: 默認啟用哈希隨機化。
-
object.__bool__(self)? 調(diào)用此方法以實現(xiàn)真值檢測以及內(nèi)置的
bool()操作;應(yīng)該返回False或True。如果未定義此方法,則會查找并調(diào)用__len__()并在其返回非零值時視對象的邏輯值為真。如果一個類既未定義__len__()也未定義__bool__()則視其所有實例的邏輯值為真。
3.3.2. 自定義屬性訪問?
可以定義下列方法來自定義對類實例屬性訪問(x.name 的使用、賦值或刪除)的具體含義.
-
object.__getattr__(self, name)? 當(dāng)默認屬性訪問因引發(fā)
AttributeError而失敗時被調(diào)用 (可能是調(diào)用__getattribute__()時由于 name 不是一個實例屬性或self的類關(guān)系樹中的屬性而引發(fā)了AttributeError;或者是對 name 特性屬性調(diào)用__get__()時引發(fā)了AttributeError)。此方法應(yīng)當(dāng)返回(找到的)屬性值或是引發(fā)一個AttributeError異常。請注意如果屬性是通過正常機制找到的,
__getattr__()就不會被調(diào)用。(這是在__getattr__()和__setattr__()之間故意設(shè)置的不對稱性。)這既是出于效率理由也是因為不這樣設(shè)置的話__getattr__()將無法訪問實例的其他屬性。要注意至少對于實例變量來說,你不必在實例屬性字典中插入任何值(而是通過插入到其他對象)就可以模擬對它的完全控制。請參閱下面的__getattribute__()方法了解真正獲取對屬性訪問的完全控制權(quán)的辦法。
-
object.__getattribute__(self, name)? 此方法會無條件地被調(diào)用以實現(xiàn)對類實例屬性的訪問。如果類還定義了
__getattr__(),則后者不會被調(diào)用,除非__getattribute__()顯式地調(diào)用它或是引發(fā)了AttributeError。此方法應(yīng)當(dāng)返回(找到的)屬性值或是引發(fā)一個AttributeError異常。為了避免此方法中的無限遞歸,其實現(xiàn)應(yīng)該總是調(diào)用具有相同名稱的基類方法來訪問它所需要的任何屬性,例如object.__getattribute__(self, name)。注解
此方法在作為通過特定語法或內(nèi)置函數(shù)隱式地調(diào)用的結(jié)果的情況下查找特殊方法時仍可能會被跳過。參見 特殊方法查找。
-
object.__setattr__(self, name, value)? 此方法在一個屬性被嘗試賦值時被調(diào)用。這個調(diào)用會取代正常機制(即將值保存到實例字典)。 name 為屬性名稱, value 為要賦給屬性的值。
如果
__setattr__()想要賦值給一個實例屬性,它應(yīng)該調(diào)用同名的基類方法,例如object.__setattr__(self, name, value)。
-
object.__delattr__(self, name)? 類似于
__setattr__()但其作用為刪除而非賦值。此方法應(yīng)該僅在del obj.name對于該對象有意義時才被實現(xiàn)。
-
object.__dir__(self)? 此方法會在對相應(yīng)對象調(diào)用
dir()時被調(diào)用。返回值必須為一個序列。dir()會把返回的序列轉(zhuǎn)換為列表并對其排序。
3.3.2.1. 自定義模塊屬性訪問?
特殊名稱 __getattr__ 和 __dir__ 還可被用來自定義對模塊屬性的訪問。模塊層級的 __getattr__ 函數(shù)應(yīng)當(dāng)接受一個參數(shù),其名稱為一個屬性名,并返回計算結(jié)果值或引發(fā)一個 AttributeError。如果通過正常查找即 object.__getattribute__() 未在模塊對象中找到某個屬性,則 __getattr__ 會在模塊的 __dict__ 中查找,未找到時會引發(fā)一個 AttributeError。如果找到,它會以屬性名被調(diào)用并返回結(jié)果值。
__dir__ 函數(shù)應(yīng)當(dāng)不接受任何參數(shù),并且返回一個表示模塊中可訪問名稱的字符串序列。 此函數(shù)如果存在,將會重載一個模塊中的標(biāo)準 dir() 查找。
想要更細致地自定義模塊的行為(設(shè)置屬性和特性屬性等待),可以將模塊對象的 __class__ 屬性設(shè)置為一個 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__ 只會影響使用屬性訪問語法進行的查找 -- 直接訪問模塊全局變量(不論是通過模塊內(nèi)的代碼還是通過對模塊全局字典的引用)是不受影響的。
在 3.5 版更改: __class__ 模塊屬性改為可寫。
3.7 新版功能: __getattr__ 和 __dir__ 模塊屬性。
參見
- PEP 562 - 模塊 __getattr__ 和 __dir__
描述用于模塊的
__getattr__和__dir__函數(shù)。
3.3.2.2. 實現(xiàn)描述器?
以下方法僅當(dāng)一個包含該方法的類(稱為 描述器 類)的實例出現(xiàn)于一個 所有者 類中的時候才會起作用(該描述器必須在所有者類或其某個上級類的字典中)。在以下示例中,“屬性”指的是名稱為所有者類 __dict__ 中的特征屬性的鍵名的屬性。
-
object.__get__(self, instance, owner)? 調(diào)用此方法以獲取所有者類的屬性(類屬性訪問)或該類的實例的屬性(實例屬性訪問)。所有者 是指所有者類,而 實例 是指被用來訪問屬性的實例,如果是 所有者 被用來訪問屬性時則為
None。此方法應(yīng)當(dāng)返回(計算出的)屬性值或是引發(fā)一個AttributeError異常。
-
object.__set__(self, instance, value)? 調(diào)用此方法以設(shè)置 instance 指定的所有者類的實例的屬性為新值 value。
-
object.__delete__(self, instance)? 調(diào)用此方法以刪除 instance 指定的所有者類的實例的屬性。
-
object.__set_name__(self, owner, name)? 在所有者類 owner 創(chuàng)建時被調(diào)用。描述器會被賦值給 name。
注解
__set_name__()只是作為type構(gòu)造器的一部分被隱式地調(diào)用,因此在某個類被初次創(chuàng)建之后又額外添加一個描述器時,那就需要顯式地調(diào)用它并且附帶適當(dāng)?shù)男螀?class A: pass descr = custom_descriptor() A.attr = descr descr.__set_name__(A, 'attr')
詳情參見 創(chuàng)建類對象。
3.6 新版功能.
屬性 __objclass__ 會被 inspect 模塊解讀為指定此對象定義所在的類(正確設(shè)置此屬性有助于動態(tài)類屬性的運行時內(nèi)省)。對于可調(diào)用對象來說,它可以指明預(yù)期或要求提供一個特定類型(或子類)的實例作為第一個位置參數(shù)(例如,CPython 會為實現(xiàn)于 C 中的未綁定方法設(shè)置此屬性)。
3.3.2.3. 發(fā)起調(diào)用描述器?
總的說來,描述器就是具有“綁定行為”的對象屬性,其屬性訪問已被描述器協(xié)議中的方法所重載,包括 __get__(), __set__() 和 __delete__()。如果一個對象定義了以上方法中的任意一個,它就被稱為描述器。
屬性訪問的默認行為是從一個對象的字典中獲取、設(shè)置或刪除屬性。例如,a.x 的查找順序會從 a.__dict__['x'] 開始,然后是 type(a).__dict__['x'],接下來依次查找 type(a) 的上級基類,不包括元類。
但是,如果找到的值是定義了某個描述器方法的對象,則 Python 可能會重載默認行為并轉(zhuǎn)而發(fā)起調(diào)用描述器方法。這具體發(fā)生在優(yōu)先級鏈的哪個環(huán)節(jié)則要根據(jù)所定義的描述器方法及其被調(diào)用的方式來決定。
描述器發(fā)起調(diào)用的開始點是一個綁定 a.x。參數(shù)的組合方式依 a 而定:
- 直接調(diào)用
最簡單但最不常見的調(diào)用方式是用戶代碼直接發(fā)起調(diào)用一個描述器方法:
x.__get__(a)。- 實例綁定
如果綁定到一個對象實例,
a.x會被轉(zhuǎn)換為調(diào)用:type(a).__dict__['x'].__get__(a, type(a))。- 類綁定
如果綁定到一個類,
A.x會被轉(zhuǎn)換為調(diào)用:A.__dict__['x'].__get__(None, A)。- 超綁定
如果
a是super的一個實例,則綁定super(B, obj).m()會在obj.__class__.__mro__中搜索B的直接上級基類A然后通過以下調(diào)用發(fā)起調(diào)用描述器:A.__dict__['m'].__get__(obj, obj.__class__)。
對于實例綁定,發(fā)起描述器調(diào)用的優(yōu)先級取決于定義了哪些描述器方法。一個描述器可以定義 __get__()、__set__() 和 __delete__() 的任意組合。如果它沒有定義 __get__(),則訪問屬性會返回描述器對象自身,除非對象的實例字典中有相應(yīng)屬性值。如果描述器定義了 __set__() 和/或 __delete__(),則它是一個數(shù)據(jù)描述器;如果以上兩個都未定義,則它是一個非數(shù)據(jù)描述器。通常,數(shù)據(jù)描述器會同時定義 __get__() 和 __set__(),而非數(shù)據(jù)描述器只有 __get__() 方法。定義了 __set__() 和 __get__() 的數(shù)據(jù)描述器總是會重載實例字典中的定義。與之相對的,非數(shù)據(jù)描述器可被實例所重載。
Python 方法 (包括 staticmethod() 和 classmethod()) 都是作為非數(shù)據(jù)描述器來實現(xiàn)的。 因此實例可以重定義并重載方法。 這允許單個實例獲得與相同類的其他實例不一樣的行為。
property() 函數(shù)是作為數(shù)據(jù)描述器來實現(xiàn)的。因此實例不能重載特性屬性的行為。
3.3.2.4. __slots__?
__slots__ 允許我們顯式地聲明數(shù)據(jù)成員(例如特征屬性)并禁止創(chuàng)建 __dict__ 和 __weakref__ (除非是在 __slots__ 中顯式地聲明或是在父類中可用。)
相比使用 __dict__ 此方式可以顯著地節(jié)省空間。 屬性查找速度也可得到顯著的提升。
-
object.__slots__? 這個類變量可賦值為字符串、可迭代對象或由實例使用的變量名構(gòu)成的字符串序列。 __slots__ 會為已聲明的變量保留空間,并阻止自動為每個實例創(chuàng)建 __dict__ 和 __weakref__。
3.3.2.4.1. 使用 __slots__ 的注意事項?
當(dāng)繼承自一個未定義 __slots__ 的類時,實例的 __dict__ 和 __weakref__ 屬性將總是可訪問。
沒有 __dict__ 變量,實例就不能給未在 __slots__ 定義中列出的新變量賦值。嘗試給一個未列出的變量名賦值將引發(fā)
AttributeError。新變量需要動態(tài)賦值,就要將'__dict__'加入到 __slots__ 聲明的字符串序列中。如果未給每個實例設(shè)置 __weakref__ 變量,定義了 __slots__ 的類就不支持對其實際的弱引用。如果需要弱引用支持,就要將
'__weakref__'加入到 __slots__ 聲明的字符串序列中。__slots__ 是通過為每個變量名創(chuàng)建描述器 (實現(xiàn)描述器) 在類層級上實現(xiàn)的。因此,類屬性不能被用來為通過 __slots__ 定義的實例變量設(shè)置默認值;否則,類屬性就會覆蓋描述器賦值。
__slots__ 聲明的作用不只限于定義它的類。在父類中聲明的 __slots__ 在其子類中同樣可用。不過,子類將會獲得 __dict__ 和 __weakref__ 除非它們也定義了 __slots__ (其中應(yīng)該僅包含對任何 額外 名稱的聲明位置)。
如果一個類定義的位置在某個基類中也有定義,則由基類位置定義的實例變量將不可訪問(除非通過直接從基類獲取其描述器的方式)。這會使得程序的含義變成未定義。未來可能會添加一個防止此情況的檢查。
非空的 __slots__ 不適用于派生自“可變長度”內(nèi)置類型例如
int、bytes和tuple的派生類。任何非字符串可迭代對象都可以被賦值給 __slots__。映射也可以被使用;不過,未來可能會分別賦給每個鍵具有特殊含義的值。
__class__ 賦值僅在兩個類具有相同的 __slots__ 時才會起作用。
帶有多個父類聲明位置的多重繼承也是可用的,但僅允許一個父類具有由聲明位置創(chuàng)建的屬性(其他基類必須具有空的位置布局) —— 違反規(guī)則將引發(fā)
TypeError。如果為 __slots__ 使用了一個迭代器,則會為迭代器的每個值創(chuàng)建描述器。 但是 __slots__ 屬性將為一個空迭代器。
3.3.3. 自定義類創(chuàng)建?
當(dāng)一個類繼承其他類時,那個類的 __init_subclass__ 會被調(diào)用。這樣就可以編寫能夠改變子類行為的類。這與類裝飾器有緊密的關(guān)聯(lián),但是類裝飾器是影響它們所應(yīng)用的特定類,而 __init_subclass__ 則只作用于定義了該方法的類所派生的子類。
-
classmethod
object.__init_subclass__(cls)? 當(dāng)所在類派生子類時此方法就會被調(diào)用。cls 將指向新的子類。如果定義為一個普通實例方法,此方法將被隱式地轉(zhuǎn)換為類方法。
傳入一個新類的關(guān)鍵字參數(shù)會被傳給父類的
__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__的默認實現(xiàn)什么都不做,只在帶任意參數(shù)調(diào)用時引發(fā)一個錯誤。注解
元類提示
metaclass將被其它類型機制消耗掉,并不會被傳給__init_subclass__的實現(xiàn)。實際的元類(而非顯式的提示)可通過type(cls)訪問。3.6 新版功能.
3.3.3.1. 元類?
默認情況下,類是使用 type() 來構(gòu)建的。類體會在一個新的命名空間內(nèi)執(zhí)行,類名會被局部綁定到 type(name, bases, namespace) 的結(jié)果。
類創(chuàng)建過程可通過在定義行傳入 metaclass 關(guān)鍵字參數(shù),或是通過繼承一個包含此參數(shù)的現(xiàn)有類來進行定制。在以下示例中,MyClass 和 MySubclass 都是 Meta 的實例:
class Meta(type):
pass
class MyClass(metaclass=Meta):
pass
class MySubclass(MyClass):
pass
在類定義內(nèi)指定的任何其他關(guān)鍵字參數(shù)都會在下面所描述的所有元類操作中進行傳遞。
當(dāng)一個類定義被執(zhí)行時,將發(fā)生以下步驟:
解析 MRO 條目;
確定適當(dāng)?shù)脑悾?/p>
準備類命名空間;
執(zhí)行類主體;
創(chuàng)建類對象。
3.3.3.2. 解析 MRO 條目?
如果在類定義中出現(xiàn)的基類不是 type 的實例,則使用 __mro_entries__ 方法對其進行搜索,當(dāng)找到結(jié)果時,它會以原始基類元組做參數(shù)進行調(diào)用。此方法必須返回類的元組以替代此基類被使用。元組可以為空,在此情況下原始基類將被忽略。
參見
PEP 560 - 對 typing 模塊和泛型類型的核心支持
3.3.3.3. 確定適當(dāng)?shù)脑?a class="headerlink" href="#determining-the-appropriate-metaclass" title="永久鏈接至標(biāo)題">?
為一個類定義確定適當(dāng)?shù)脑愂歉鶕?jù)以下規(guī)則:
如果沒有基類且沒有顯式指定元類,則使用
type();如果給出一個顯式元類而且 不是
type()的實例,則其會被直接用作元類;如果給出一個
type()的實例作為顯式元類,或是定義了基類,則使用最近派生的元類。
最近派生的元類會從顯式指定的元類(如果有)以及所有指定的基類的元類(即 type(cls))中選取。最近派生的元類應(yīng)為 所有 這些候選元類的一個子類型。如果沒有一個候選元類符合該條件,則類定義將失敗并拋出 TypeError。
3.3.3.4. 準備類命名空間?
一旦確定了適當(dāng)?shù)脑悾瑒t將準備好類命名空間。 如果元類具有 __prepare__ 屬性,它會以 namespace = metaclass.__prepare__(name, bases, **kwds) 的形式被調(diào)用(其中如果有任何額外的關(guān)鍵字參數(shù),則應(yīng)當(dāng)來自類定義)。 __prepare__ 方法應(yīng)該被實現(xiàn)為 classmethod()。 __prepare__ 所返回的命名空間會被傳入 __new__,但是當(dāng)最終的類對象被創(chuàng)建時,該命名空間會被拷貝到一個新的 dict 中。
如果元類沒有 __prepare__ 屬性,則類命名空間將初始化為一個空的有序映射。
參見
- PEP 3115 - Python 3000 中的元類
引入
__prepare__命名空間鉤子
3.3.3.5. 執(zhí)行類主體?
類主體會以(類似于) exec(body, globals(), namespace) 的形式被執(zhí)行。普通調(diào)用與 exec() 的關(guān)鍵區(qū)別在于當(dāng)類定義發(fā)生于函數(shù)內(nèi)部時,詞法作用域允許類主體(包括任何方法)引用來自當(dāng)前和外部作用域的名稱。
但是,即使當(dāng)類定義發(fā)生于函數(shù)內(nèi)部時,在類內(nèi)部定義的方法仍然無法看到在類作用域?qū)哟紊隙x的名稱。類變量必須通過實例的第一個形參或類方法來訪問,或者是通過下一節(jié)中描述的隱式詞法作用域的 __class__ 引用。
3.3.3.6. 創(chuàng)建類對象?
一旦執(zhí)行類主體完成填充類命名空間,將通過調(diào)用 metaclass(name, bases, namespace, **kwds) 創(chuàng)建類對象(此處的附加關(guān)鍵字參數(shù)與傳入 __prepare__ 的相同)。
如果類主體中有任何方法引用了 __class__ 或 super,這個類對象會通過零參數(shù)形式的 super(). __class__ 所引用,這是由編譯器所創(chuàng)建的隱式閉包引用。這使用零參數(shù)形式的 super() 能夠正確標(biāo)識正在基于詞法作用域來定義的類,而被用于進行當(dāng)前調(diào)用的類或?qū)嵗齽t是基于傳遞給方法的第一個參數(shù)來標(biāo)識的。
CPython implementation detail: 在 CPython 3.6 及之后的版本中,__class__ 單元會被作為類命名空間中的 __classcell__ 一項傳遞給元類。如果存在,這必須被向上傳播給 type.__new__ 調(diào)用,以便能正確地初始化該類。如果不這樣做,在 Python 3.6 中將導(dǎo)致 DeprecationWarning,而在 Python 3.8 中將引發(fā) RuntimeError。
當(dāng)使用默認的元類 type 或者任何最終會調(diào)用 type.__new__ 的元類時,以下額外的自定義步驟將在創(chuàng)建類對象之后被發(fā)起調(diào)用:
首先,
type.__new__將收集類命名空間中所有定義了__set_name__()方法的描述器;接下來,所有這些
__set_name__方法將使用所定義的類和特定描述器所賦的名稱進行調(diào)用;最后,將在新類根據(jù)方法解析順序所確定的直接父類上調(diào)用
__init_subclass__()鉤子。
在類對象創(chuàng)建之后,它會被傳給包含在類定義中的類裝飾器(如果有的話),得到的對象將作為已定義的類綁定到局部命名空間。
當(dāng)通過 type.__new__ 創(chuàng)建一個新類時,提供以作為命名空間形參的對象會被復(fù)制到一個新的有序映射并丟棄原對象。這個新副本包裝于一個只讀代理中,后者則成為類對象的 __dict__ 屬性。
參見
- PEP 3135 - 新的超類型
描述隱式的
__class__閉包引用
3.3.3.7. 元類的作用?
元類的潛在作用非常廣泛。已經(jīng)過嘗試的設(shè)想包括枚舉、日志、接口檢查、自動委托、自動特征屬性創(chuàng)建、代理、框架以及自動資源鎖定/同步等等。
3.3.4. 自定義實例及子類檢查?
以下方法被用來重載 isinstance() 和 issubclass() 內(nèi)置函數(shù)的默認行為。
特別地,元類 abc.ABCMeta 實現(xiàn)了這些方法以便允許將抽象基類(ABC)作為“虛擬基類”添加到任何類或類型(包括內(nèi)置類型),包括其他 ABC 之中。
-
class.__instancecheck__(self, instance)? 如果 instance 應(yīng)被視為 class 的一個(直接或間接)實例則返回真值。如果定義了此方法,則會被調(diào)用以實現(xiàn)
isinstance(instance, class)。
-
class.__subclasscheck__(self, subclass)? Return true 如果 subclass 應(yīng)被視為 class 的一個(直接或間接)子類則返回真值。如果定義了此方法,則會被調(diào)用以實現(xiàn)
issubclass(subclass, class)。
請注意這些方法的查找是基于類的類型(元類)。它們不能作為類方法在實際的類中被定義。這與基于實例被調(diào)用的特殊方法的查找是一致的,只有在此情況下實例本身被當(dāng)作是類。
參見
- PEP 3119 - 引入抽象基類
新增功能描述,通過
__instancecheck__()和__subclasscheck__()來定制isinstance()和issubclass()行為,加入此功能的動機是出于向該語言添加抽象基類的內(nèi)容(參見abc模塊)。
3.3.5. 模擬泛型類型?
通過定義一個特殊方法,可以實現(xiàn)由 PEP 484 所規(guī)定的泛型類語法 (例如 List[int]):
-
classmethod
object.__class_getitem__(cls, key)? 按照 key 參數(shù)指定的類型返回一個表示泛型類的專門化對象。
此方法的查找會基于對象自身,并且當(dāng)定義于類體內(nèi)部時,此方法將隱式地成為類方法。請注意,此機制主要是被保留用于靜態(tài)類型提示,不鼓勵在其他場合使用。
參見
PEP 560 - 對 typing 模塊和泛型類型的核心支持
3.3.6. 模擬可調(diào)用對象?
-
object.__call__(self[, args...])? 此方法會在實例作為一個函數(shù)被“調(diào)用”時被調(diào)用;如果定義了此方法,則
x(arg1, arg2, ...)就相當(dāng)于x.__call__(arg1, arg2, ...)的快捷方式。
3.3.7. 模擬容器類型?
可以定義下列方法來實現(xiàn)容器對象。 容器通常屬于序列(如列表或元組)或映射(如字典),但也存在其他形式的容器。 前幾個方法集被用于模擬序列或是模擬映射;兩者的不同之處在于序列允許的鍵應(yīng)為整數(shù) k 且 0 <= k < N 其中 N 是序列或定義指定區(qū)間的項的切片對象的長度。 此外還建議讓映射提供 keys(), values(), items(), get(), clear(), setdefault(), pop(), popitem(), copy() 以及 update() 等方法,它們的行為應(yīng)與 Python 標(biāo)準字典對象的相應(yīng)方法類似。 此外 collections.abc 模塊提供了一個 MutableMapping 抽象基類以便根據(jù)由 __getitem__(), __setitem__(), __delitem__(), 和 keys() 組成的基本集來創(chuàng)建所需的方法。 可變序列還應(yīng)像 Python 標(biāo)準列表對象那樣提供 append(), count(), index(), extend(), insert(), pop(), remove(), reverse() 和 sort() 等方法。 最后,序列類型還應(yīng)通過定義下文描述的 __add__(), __radd__(), __iadd__(), __mul__(), __rmul__() 和 __imul__() 等方法來實現(xiàn)加法(指拼接)和乘法(指重復(fù));它們不應(yīng)定義其他數(shù)值運算符。 此外還建議映射和序列都實現(xiàn) __contains__() 方法以允許高效地使用 in 運算符;對于映射,in 應(yīng)該搜索映射的鍵;對于序列,則應(yīng)搜索其中的值。 另外還建議映射和序列都實現(xiàn) __iter__() 方法以允許高效地迭代容器中的條目;對于映射,__iter__() 應(yīng)當(dāng)?shù)鷮ο蟮逆I;對于序列,則應(yīng)當(dāng)?shù)渲械闹怠?/p>
-
object.__len__(self)? 調(diào)用此方法以實現(xiàn)內(nèi)置函數(shù)
len()。應(yīng)該返回對象的長度,以一個>=0 的整數(shù)表示。此外,如果一個對象未定義__bool__()方法而其__len__()方法返回值為零,則在布爾運算中會被視為假值。CPython implementation detail: 在 CPython 中,要求長度最大為
sys.maxsize。如果長度大于sys.maxsize則某些特性 (例如len()) 可能會引發(fā)OverflowError。要通過真值檢測來防止引發(fā)OverflowError,對象必須定義__bool__()方法。
-
object.__length_hint__(self)? 調(diào)用此方法以實現(xiàn)
operator.length_hint()。 應(yīng)該返回對象長度的估計值(可能大于或小于實際長度)。 此長度應(yīng)為一個>=0 的整數(shù)。 返回值也可以為NotImplemented,這會被視作與__length_hint__方法完全不存在時一樣處理。 此方法純粹是為了優(yōu)化性能,并不要求正確無誤。3.4 新版功能.
注解
切片是通過下述三個專門方法完成的。以下形式的調(diào)用
a[1:2] = b
會為轉(zhuǎn)寫為
a[slice(1, 2, None)] = b
其他形式以此類推。略去的切片項總是以 None 補全。
-
object.__getitem__(self, key)? 調(diào)用此方法以實現(xiàn)
self[key]的求值。對于序列類型,接受的鍵應(yīng)為整數(shù)和切片對象。請注意負數(shù)索引(如果類想要模擬序列類型)的特殊解讀是取決于__getitem__()方法。如果 key 的類型不正確則會引發(fā)TypeError異常;如果為序列索引集范圍以外的值(在進行任何負數(shù)索引的特殊解讀之后)則應(yīng)引發(fā)IndexError異常。對于映射類型,如果 key 找不到(不在容器中)則應(yīng)引發(fā)KeyError異常。注解
for循環(huán)在有不合法索引時會期待捕獲IndexError以便正確地檢測到序列的結(jié)束。
-
object.__setitem__(self, key, value)? 調(diào)用此方法以實現(xiàn)向
self[key]賦值。注意事項與__getitem__()相同。為對象實現(xiàn)此方法應(yīng)該僅限于需要映射允許基于鍵修改值或添加鍵,或是序列允許元素被替換時。不正確的 key 值所引發(fā)的異常應(yīng)與__getitem__()方法的情況相同。
-
object.__delitem__(self, key)? 調(diào)用此方法以實現(xiàn)
self[key]的刪除。注意事項與__getitem__()相同。為對象實現(xiàn)此方法應(yīng)該權(quán)限于需要映射允許移除鍵,或是序列允許移除元素時。不正確的 key 值所引發(fā)的異常應(yīng)與__getitem__()方法的情況相同。
-
object.__missing__(self, key)? 此方法由
dict.__getitem__()在找不到字典中的鍵時調(diào)用以實現(xiàn) dict 子類的self[key]。
-
object.__iter__(self)? 此方法在需要為容器創(chuàng)建迭代器時被調(diào)用。此方法應(yīng)該返回一個新的迭代器對象,它能夠逐個迭代容器中的所有對象。對于映射,它應(yīng)該逐個迭代容器中的鍵。
迭代器對象也需要實現(xiàn)此方法;它們需要返回對象自身。有關(guān)迭代器對象的詳情請參看 迭代器類型 一節(jié)。
-
object.__reversed__(self)? 此方法(如果存在)會被
reversed()內(nèi)置函數(shù)調(diào)用以實現(xiàn)逆向迭代。它應(yīng)當(dāng)返回一個新的以逆序逐個迭代容器內(nèi)所有對象的迭代器對象。如果未提供
__reversed__()方法,則reversed()內(nèi)置函數(shù)將回退到使用序列協(xié)議 (__len__()和__getitem__())。支持序列協(xié)議的對象應(yīng)當(dāng)僅在能夠提供比reversed()所提供的實現(xiàn)更高效的實現(xiàn)時才提供__reversed__()方法。
成員檢測運算符 (in 和 not in) 通常以對容器進行逐個迭代的方式來實現(xiàn)。 不過,容器對象可以提供以下特殊方法并采用更有效率的實現(xiàn),這樣也不要求對象必須為可迭代對象。
-
object.__contains__(self, item)? 調(diào)用此方法以實現(xiàn)成員檢測運算符。如果 item 是 self 的成員則應(yīng)返回真,否則返回假。對于映射類型,此檢測應(yīng)基于映射的鍵而不是值或者鍵值對。
對于未定義
__contains__()的對象,成員檢測將首先嘗試通過__iter__()進行迭代,然后再使用__getitem__()的舊式序列迭代協(xié)議,參看 語言參考中的相應(yīng)部分。
3.3.8. 模擬數(shù)字類型?
定義以下方法即可模擬數(shù)字類型。特定種類的數(shù)字不支持的運算(例如非整數(shù)不能進行位運算)所對應(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)用這些方法來實現(xiàn)二進制算術(shù)運算 (
+,-,*,@,/,//,%,divmod(),pow(),**,<<,>>,&,^,|)。例如,求表達式x + y的值,其中 x 是具有__add__()方法的類的一個實例,則會調(diào)用x.__add__(y)。__divmod__()方法應(yīng)該等價于使用__floordiv__()和__mod__(),它不應(yīng)該被關(guān)聯(lián)到__truediv__()。請注意如果要支持三元版本的內(nèi)置pow()函數(shù),則__pow__()的定義應(yīng)該接受可選的第三個參數(shù)。如果這些方法中的某一個不支持與所提供參數(shù)進行運算,它應(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)用這些方法來實現(xiàn)具有反射(交換)操作數(shù)的二進制算術(shù)運算 (
+,-,*,@,/,//,%,divmod(),pow(),**,<<,>>,&,^,|)。這些成員函數(shù)僅會在左操作數(shù)不支持相應(yīng)運算 3 且兩個操作數(shù)類型不同時被調(diào)用。4 例如,求表達式x - y的值,其中 y 是具有__rsub__()方法的類的一個實例,則當(dāng)x.__sub__(y)返回 NotImplemented 時會調(diào)用y.__rsub__(x)。請注意三元版的
pow()并不會嘗試調(diào)用__rpow__()(因為強制轉(zhuǎn)換規(guī)則會太過復(fù)雜)。注解
如果右操作數(shù)類型為左操作數(shù)類型的一個子類,且該子類提供了指定運算的反射方法,則此方法會先于左操作數(shù)的非反射方法被調(diào)用。此行為可允許子類重載其祖先類的運算符。
-
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)用這些方法來實現(xiàn)擴展算術(shù)賦值 (
+=,-=,*=,@=,/=,//=,%=,**=,<<=,>>=,&=,^=,|=)。這些方法應(yīng)該嘗試進行自身操作 (修改 self) 并返回結(jié)果 (結(jié)果應(yīng)該但并非必須為 self)。如果某個方法未被定義,相應(yīng)的擴展算術(shù)賦值將回退到普通方法。例如,如果 x 是具有__iadd__()方法的類的一個實例,則x += y就等價于x = x.__iadd__(y)。否則就如x + y的求值一樣選擇x.__add__(y)和y.__radd__(x)。在某些情況下,擴展賦值可導(dǎo)致未預(yù)期的錯誤 (參見 為什么 a_tuple[i] += ['item'] 會在執(zhí)行加法時引發(fā)異常?),但此行為實際上是數(shù)據(jù)模型的一個組成部分。
-
object.__neg__(self)? -
object.__pos__(self)? -
object.__abs__(self)? -
object.__invert__(self)? 調(diào)用此方法以實現(xiàn)一元算術(shù)運算 (
-,+,abs()和~)。
-
object.__complex__(self)? -
object.__int__(self)? -
object.__float__(self)? 調(diào)用這些方法以實現(xiàn)內(nèi)置函數(shù)
complex(),int()和float()。應(yīng)當(dāng)返回一個相應(yīng)類型的值。
-
object.__index__(self)? 調(diào)用此方法以實現(xiàn)
operator.index()以及 Python 需要無損地將數(shù)字對象轉(zhuǎn)換為整數(shù)對象的場合(例如切片或是內(nèi)置的bin(),hex()和oct()函數(shù))。 存在此方法表明數(shù)字對象屬于整數(shù)類型。 必須返回一個整數(shù)。注解
為了具有一致的整數(shù)類型類,當(dāng)定義了
__index__()的時候也應(yīng)當(dāng)定義__int__(),兩者應(yīng)當(dāng)返回相同的值。
-
object.__round__(self[, ndigits])? -
object.__trunc__(self)? -
object.__floor__(self)? -
object.__ceil__(self)? 調(diào)用這些方法以實現(xiàn)內(nèi)置函數(shù)
round()以及math函數(shù)trunc(),floor()和ceil()。 除了將 ndigits 傳給__round__()的情況之外這些方法的返回值都應(yīng)當(dāng)是原對象截斷為Integral(通常為int)。如果未定義
__int__()則內(nèi)置函數(shù)int()會回退到__trunc__()。
3.3.9. with 語句上下文管理器?
上下文管理器 是一個對象,它定義了在執(zhí)行 with 語句時要建立的運行時上下文。 上下文管理器處理進入和退出所需運行時上下文以執(zhí)行代碼塊。 通常使用 with 語句(在 with 語句 中描述),但是也可以通過直接調(diào)用它們的方法來使用。
上下文管理器的典型用法包括保存和恢復(fù)各種全局狀態(tài),鎖定和解鎖資源,關(guān)閉打開的文件等等。
要了解上下文管理器的更多信息,請參閱 上下文管理器類型。
-
object.__exit__(self, exc_type, exc_value, traceback)? 退出關(guān)聯(lián)到此對象的運行時上下文。 各個參數(shù)描述了導(dǎo)致上下文退出的異常。 如果上下文是無異常地退出的,三個參數(shù)都將為
None。如果提供了異常,并且希望方法屏蔽此異常(即避免其被傳播),則應(yīng)當(dāng)返回真值。 否則的話,異常將在退出此方法時按正常流程處理。
請注意
__exit__()方法不應(yīng)該重新引發(fā)被傳入的異常,這是調(diào)用者的責(zé)任。
3.3.10. 特殊方法查找?
對于自定義類來說,特殊方法的隱式發(fā)起調(diào)用僅保證在其定義于對象類型中時能正確地發(fā)揮作用,而不能定義在對象實例字典中。 該行為就是以下代碼會引發(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()
此行為背后的原理在于包括類型對象在內(nèi)的所有對象都會實現(xiàn)的幾個特殊方法,例如 __hash__() 和 __repr__()。 如果這些方法的隱式查找使用了傳統(tǒng)的查找過程,它們會在對類型對象本身發(fā)起調(diào)用時失敗:
>>> 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)用一個類的未綁定方法有時被稱為‘元類混淆’,可以通過在查找特殊方法時繞過實例的方式來避免:
>>> 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__() 機制為解析器內(nèi)部的速度優(yōu)化提供了顯著的空間,其代價則是犧牲了處理特殊方法時的一些靈活性(特殊方法 必須 設(shè)置在類對象本身上以便始終一致地由解釋器發(fā)起調(diào)用)。
3.4. 協(xié)程?
3.4.1. 可等待對象?
awaitable 對象主要實現(xiàn)了 __await__() 方法。 從 async def 函數(shù)返回的 Coroutine 對象即屬于可等待對象。
注解
從帶有 types.coroutine() 或 asyncio.coroutine() 裝飾器的生成器返回的 generator iterator 對象也屬于可等待對象,但它們并未實現(xiàn) __await__()。
-
object.__await__(self)? 必須返回一個 iterator。 應(yīng)當(dāng)被用來實現(xiàn) awaitable 對象。 例如,
asyncio.Future實現(xiàn)了此方法以與await表達式相兼容。
3.5 新版功能.
參見
PEP 492 了解有關(guān)可等待對象的詳細信息。
3.4.2. 協(xié)程對象?
Coroutine 對象屬于 awaitable 對象。 協(xié)程的執(zhí)行可通過調(diào)用 __await__() 并迭代其結(jié)果來進行控制。 當(dāng)協(xié)程結(jié)束執(zhí)行并返回時,迭代器會引發(fā) StopIteration,該異常的 value 屬性將指向返回值。 如果協(xié)程引發(fā)了異常,它會被迭代器所傳播。 協(xié)程不應(yīng)該直接引發(fā)未處理的 StopIteration 異常。
協(xié)程也具有下面列出的方法,它們類似于生成器的對應(yīng)方法 (參見 生成器-迭代器的方法)。 但是,與生成器不同,協(xié)程并不直接支持迭代。
在 3.5.2 版更改: 等待一個協(xié)程超過一次將引發(fā) RuntimeError。
-
coroutine.send(value)? 開始或恢復(fù)協(xié)程的執(zhí)行。 如果 value 為
None,則這相當(dāng)于前往__await__()所返回迭代器的下一項。 如果 value 不為None,此方法將委托給導(dǎo)致協(xié)程掛起的迭代器的send()方法。 其結(jié)果(返回值,StopIteration或是其他異常)將與上述對__await__()返回值進行迭代的結(jié)果相同。
-
coroutine.throw(type[, value[, traceback]])? 在協(xié)程內(nèi)引發(fā)指定的異常。 此方法將委托給導(dǎo)致協(xié)程掛起的迭代器的
throw()方法,如果存在該方法。 否則的話,異常會在掛起點被引發(fā)。 其結(jié)果(返回值,StopIteration或是其他異常)將與上述對__await__()返回值進行迭代的結(jié)果相同。 如果異常未在協(xié)程內(nèi)被捕獲,則將回傳給調(diào)用者。
-
coroutine.close()? 此方法會使得協(xié)程清理自身并退出。 如果協(xié)程被掛起,此方法會先委托給導(dǎo)致協(xié)程掛起的迭代器的
close()方法,如果存在該方法。 然后它會在掛起點引發(fā)GeneratorExit,使得協(xié)程立即清理自身。 最后,協(xié)程會被標(biāo)記為已結(jié)束執(zhí)行,即使它根本未被啟動。當(dāng)協(xié)程對象將要被銷毀時,會使用以上處理過程來自動關(guān)閉。
3.4.3. 異步迭代器?
異步迭代器 可以在其 __anext__ 方法中調(diào)用異步代碼。
異步迭代器可在 async for 語句中使用。
-
object.__aiter__(self)? 必須返回一個 異步迭代器 對象。
-
object.__anext__(self)? 必須返回一個 可迭代對象 輸出迭代器的下一結(jié)果值。 當(dāng)?shù)Y(jié)束時應(yīng)該引發(fā)
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.4.4. 異步上下文管理器?
異步上下文管理器 是 上下文管理器 的一種,它能夠在其 __aenter__ 和 __aexit__ 方法中暫停執(zhí)行。
異步上下文管理器可在 async with 語句中使用。
-
object.__aenter__(self)? 此方法在語義上類似于
__enter__(),僅有的區(qū)別是它必須返回一個 可等待對象。
-
object.__aexit__(self, exc_type, exc_value, traceback)? 此方法在語義上類似于
__exit__(),僅有的區(qū)別是它必須返回一個 可等待對象。
異步上下文管理器類的一個示例:
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
在某些情況下 有可能 基于可控的條件改變一個對象的類型。 但這通常不是個好主意,因為如果處理不當(dāng)會導(dǎo)致一些非常怪異的行為。
- 2
__hash__(),__iter__(),__reversed__()以及__contains__()方法對此有特殊處理;其他方法仍會引發(fā)TypeError,但可能依靠None屬于不可調(diào)用對象的行為來做到這一點。- 3
這里的“不支持”是指該類無此方法,或方法返回
NotImplemented。 如果你想強制回退到右操作數(shù)的反射方法,請不要設(shè)置方法為None— 那會造成顯式地 阻塞 此種回退的相反效果。- 4
對于相同類型的操作數(shù),如果非反射方法 (例如
__add__()) 失敗則會認為相應(yīng)運算不被支持,這就是反射方法未被調(diào)用的原因。
