來自:http://blog.163.com/jackylau_v/blog/static/175754040201182113817834/
2011-09-02 23:38:17| 分類: 派森程序點(diǎn)滴 |字號 訂閱
Python 用下劃線作為變量前綴和后綴指定特殊變量
_xxx 不能用’from module import *’導(dǎo)入
__xxx__ 系統(tǒng)定義名字
__xxx 類中的私有變量名
核心風(fēng)格:避免用下劃線作為變量名的開始。
因?yàn)橄聞澗€對解釋器有特殊的意義,而且是內(nèi)建標(biāo)識符所使用的符號,我們建議程序員避免用下劃線作為變量名的開始。一般來講,變量名_xxx被看作是“私有 的”,在模塊或類外不可以使用。當(dāng)變量是私有的時(shí)候,用_xxx 來表示變量是很好的習(xí)慣。因?yàn)樽兞棵鸰_xxx__對Python 來說有特殊含義,對于普通的變量應(yīng)當(dāng)避免這種命名風(fēng)格。
“單下劃線” 開始的成員變量叫做保護(hù)變量,意思是只有類對象和子類對象自己能訪問到這些變量;
“雙下劃線” 開始的是私有成員,意思是只有類對象自己能訪問,連子類對象也不能訪問到這個(gè)數(shù)據(jù)。
以單下劃線開頭(_foo)的代表不能直接訪問的類屬性,需通過類提供的接口進(jìn)行訪問,不能用“from xxx import *”而導(dǎo)入;以雙下劃線開頭的(__foo)代表類的私有成員;以雙下劃線開頭和結(jié)尾的(__foo__)代表python里特殊方法專用的標(biāo)識,如 __init__()代表類的構(gòu)造函數(shù)。
現(xiàn)在我們來總結(jié)下所有的系統(tǒng)定義屬性和方法, 先來看下保留屬性:
>>> Class1.__doc__ # 類型幫助信息 'Class1 Doc.' >>> Class1.__name__ # 類型名稱 'Class1' >>> Class1.__module__ # 類型所在模塊 '__main__' >>> Class1.__bases__ # 類型所繼承的基類 (<type 'object'>,) >>> Class1.__dict__ # 類型字典,存儲所有類型成員信息。 <dictproxy object at 0x00D3AD70> >>> Class1().__class__ # 類型 <class '__main__.Class1'> >>> Class1().__module__ # 實(shí)例類型所在模塊 '__main__' >>> Class1().__dict__ # 對象字典,存儲所有實(shí)例成員信息。 {'i': 1234}
接下來是保留方法,可以把保留方法分類:
序號 | 目的 | 所編寫代碼 | Python 實(shí)際調(diào)用 |
---|---|---|---|
① | 初始化一個(gè)實(shí)例 | x = MyClass() | x.__init__() |
② | 字符串的“官方”表現(xiàn)形式 | repr(x) | x.__repr__() |
③ | 字符串的“非正式”值 | str(x) | x.__str__() |
④ | 字節(jié)數(shù)組的“非正式”值 | bytes(x) | x.__bytes__() |
⑤ | 格式化字符串的值 | format(x, format_spec) | x.__format__(format_spec) |
__init__()
方法的調(diào)用發(fā)生在實(shí)例被創(chuàng)建 之后 。如果要控制實(shí)際創(chuàng)建進(jìn)程,請使用 __new__()
方法。 __repr__()
方法所返回的字符串為合法的 Python 表達(dá)式。 print(x)
的同時(shí)也調(diào)用了 __str__()
方法。 bytes
類型的引入而從 Python 3 開始出現(xiàn)。 序號 | 目的 | 所編寫代碼 | Python 實(shí)際調(diào)用 |
---|---|---|---|
① | 遍歷某個(gè)序列 | iter(seq) | seq.__iter__() |
② | 從迭代器中獲取下一個(gè)值 | next(seq) | seq.__next__() |
③ | 按逆序創(chuàng)建一個(gè)迭代器 | reversed(seq) | seq.__reversed__() |
__iter__()
方法。這是用初始值對迭代器進(jìn)行初始化的絕佳之處。 __next__()
方法。 __reversed__()
方法并不常用。它以一個(gè)現(xiàn)有序列為參數(shù),并將該序列中所有元素從尾到頭以逆序排列生成一個(gè)新的迭代器。 計(jì)算屬性
序號 | 目的 | 所編寫代碼 | Python 實(shí)際調(diào)用 |
---|---|---|---|
① | 獲取一個(gè)計(jì)算屬性(無條件的) | x.my_property | ) |
② | 獲取一個(gè)計(jì)算屬性(后備) | x.my_property | ) |
③ | 設(shè)置某屬性 | x.my_property = value | ,value) |
④ | 刪除某屬性 | del x.my_property | ) |
⑤ | 列出所有屬性和方法 | dir(x) | x.__dir__() |
__getattribute__()
方法,在 每次引用屬性或方法名稱時(shí) Python 都調(diào)用它(特殊方法名稱除外,因?yàn)槟菢訉?dǎo)致討厭的無限循環(huán))。 __getattr__()
方法,Python 將只在正常的位置查詢屬性時(shí)才會調(diào)用它。如果實(shí)例 x 定義了屬性 color, x.color
將 不會 調(diào)用x.__getattr__('color')
;而只會返回 x.color 已定義好的值。 __setattr__()
方法。 __delattr__()
方法。 __getattr__()
或 __getattribute__()
方法, __dir__()
方法將非常有用。通常,調(diào)用 dir(x)
將只顯示正常的屬性和方法。如果 __getattr()__
方法動態(tài)處理 color 屬性, dir(x)
將不會將 color 列為可用屬性??赏ㄟ^覆蓋 __dir__()
方法允許將 color 列為可用屬性,對于想使用你的類但卻不想深入其內(nèi)部的人來說,該方法非常有益。 可以讓類的實(shí)例變得可調(diào)用——就像函數(shù)可以調(diào)用一樣——通過定義 __call__()
方法。
序號 | 目的 | 所編寫代碼 | Python 實(shí)際調(diào)用 |
---|---|---|---|
像調(diào)用函數(shù)一樣“調(diào)用”一個(gè)實(shí)例 | my_instance() | my_instance.__call__() |
zipfile
模塊 通過該方式定義了一個(gè)可以使用給定密碼解密 經(jīng)加密 zip 文件的類。該 zip 解密 算法需要在解密的過程中保存狀態(tài)。通過將解密器定義為類,使我們得以在 decryptor 類的單個(gè)實(shí)例中對該狀態(tài)進(jìn)行維護(hù)。狀態(tài)在 __init__()
方法中進(jìn)行初始化,如果文件 經(jīng)加密 則進(jìn)行更新。但由于該類像函數(shù)一樣“可調(diào)用”,因此可以將實(shí)例作為 map()
函數(shù)的第一個(gè)參數(shù)傳入,代碼如下:
# excerpt from zipfile.py class _ZipDecrypter: def __init__(self, pwd): self.key0 = 305419896 ① self.key1 = 591751049 self.key2 = 878082192 for p in pwd: self._UpdateKeys(p) def __call__(self, c): ② assert isinstance(c, int) k = self.key2 | 2 c = c ^ (((k * (k^1)) >>
& 255) self._UpdateKeys(c) return czd = _ZipDecrypter(pwd) ③ bytes = zef_file.read(12) h = list(map(zd, bytes[0:12])) ④
_ZipDecryptor
類維護(hù)了以三個(gè)旋轉(zhuǎn)密鑰形式出現(xiàn)的狀態(tài),該狀態(tài)稍后將在 _UpdateKeys()
方法中更新(此處未展示)。 __call__()
方法,使得該類可像函數(shù)一樣調(diào)用。在此例中,__call__()
對 zip 文件的單個(gè)字節(jié)進(jìn)行解密,然后基于經(jīng)解密的字節(jié)對旋轉(zhuǎn)密碼進(jìn)行更新。 _ZipDecryptor
類的一個(gè)實(shí)例。變量 pwd 被傳入 __init__()
方法,并在其中被存儲和用于首次旋轉(zhuǎn)密碼更新。 __call__()
方法 12 次,也就是 更新內(nèi)部狀態(tài)并返回結(jié)果字節(jié) 12 次。 如果類作為一系列值的容器出現(xiàn)——也就是說如果對某個(gè)類來說,是否“包含”某值是件有意義的事情——那么它也許應(yīng)該定義下面的特殊方法已,讓它的行為方式與序列類似。
序號 | 目的 | 所編寫代碼 | Python 實(shí)際調(diào)用 |
---|---|---|---|
序列的長度 | len(seq) | seq.__len__() | |
了解某序列是否包含特定的值 | x in seq | seq.__contains__(x) |
cgi
模塊 在其 FieldStorage
類中使用了這些方法,該類用于表示提交給動態(tài)網(wǎng)頁的所有表單字段或查詢參數(shù)。
# A script which responds to http://example.com/search?q=cgi import cgi fs = cgi.FieldStorage() if 'q' in fs: ① do_search() # An excerpt from cgi.py that explains how that works class FieldStorage: . . . def __contains__(self, key): ② if self.list is None: raise TypeError('not indexable') return any(item.name == key for item in self.list) ③ def __len__(self): ④ return len(self.keys()) ⑤
cgi.FieldStorage
類的實(shí)例,就可以使用 “in
” 運(yùn)算符來檢查查詢字符串中是否包含了某個(gè)特定參數(shù)。 __contains__()
方法是令該魔法生效的主角。 if 'q' in fs
,Python 將在 fs 對象中查找 __contains__()
方法,而該方法在 cgi.py
中已經(jīng)定義。'q'
的值被當(dāng)作 key 參數(shù)傳入__contains__()
方法。 FieldStorage
類還支持返回其長度,因此可以編寫代碼 len(fs)
而其將調(diào)用 FieldStorage
的 __len__()
方法,并返回其識別的查詢參數(shù)個(gè)數(shù)。 self.keys()
方法檢查 self.list is None
是否為真值,因此 __len__
方法無需重復(fù)該錯(cuò)誤檢查。 在前一節(jié)的基礎(chǔ)上稍作拓展,就不僅可以對 “in
” 運(yùn)算符和 len()
函數(shù)進(jìn)行響應(yīng),還可像全功能字典一樣根據(jù)鍵來返回值。
序號 | 目的 | 所編寫代碼 | Python 實(shí)際調(diào)用 |
---|---|---|---|
通過鍵來獲取值 | x[key] | x.__getitem__(key) | |
通過鍵來設(shè)置值 | x[key] = value | x.__setitem__(key, value) | |
刪除一個(gè)鍵值對 | del x[key] | x.__delitem__(key) | |
為缺失鍵提供默認(rèn)值 | x[nonexistent_key] | x.__missing__(nonexistent_key) |
cgi
模塊 的 FieldStorage
類 同樣定義了這些特殊方法,也就是說可以像下面這樣編碼:
# A script which responds to http://example.com/search?q=cgi import cgi fs = cgi.FieldStorage() if 'q' in fs: do_search(fs['q']) ① # An excerpt from cgi.py that shows how it works class FieldStorage: . . . def __getitem__(self, key): ② if self.list is None: raise TypeError('not indexable') found = [] for item in self.list: if item.name == key: found.append(item) if not found: raise KeyError(key) if len(found) == 1: return found[0] else: return found
cgi.FieldStorage
類的一個(gè)實(shí)例,但仍然可以像 fs['q']
這樣估算表達(dá)式。 fs['q']
將 key 參數(shù)設(shè)置為 'q'
來調(diào)用 __getitem__()
方法。然后它將在其內(nèi)部維護(hù)的查詢參數(shù)列表 (self.list) 中查找一個(gè) .name
與給定鍵相符的字典項(xiàng)。 我將此內(nèi)容從前一節(jié)中拿出來使其單獨(dú)成節(jié),是因?yàn)椤氨容^”操作并不局限于數(shù)字。許多數(shù)據(jù)類型都可以進(jìn)行比較——字符串、列表,甚至字典。如果要?jiǎng)?chuàng)建自己的類,且對象之間的比較有意義,可以使用下面的特殊方法來實(shí)現(xiàn)比較。
序號 | 目的 | 所編寫代碼 | Python 實(shí)際調(diào)用 |
---|---|---|---|
相等 | x == y | x.__eq__(y) | |
不相等 | x != y | x.__ne__(y) | |
小于 | x < y | x.__lt__(y) | |
小于或等于 | x <= y | x.__le__(y) | |
大于 | x > y | x.__gt__(y) | |
大于或等于 | x >= y | x.__ge__(y) | |
布爾上上下文環(huán)境中的真值 | if x: | x.__bool__() |
如果定義了
__lt__()
方法但沒有定義__gt__()
方法,Python 將通過經(jīng)交換的算子調(diào)用__lt__()
方法。然而,Python 并不會組合方法。例如,如果定義了__lt__()
方法和__eq()__
方法,并試圖測試是否x <= y
,Python 不會按順序調(diào)用__lt__()
和__eq()__
。它將只調(diào)用__le__()
方法。
Python 支持 任意對象的序列化和反序列化。(多數(shù) Python 參考資料稱該過程為 “pickling” 和 “unpickling”)。該技術(shù)對與將狀態(tài)保存為文件并在稍后恢復(fù)它非常有意義。所有的 內(nèi)置數(shù)據(jù)類型 均已支持 pickling 。如果創(chuàng)建了自定義類,且希望它能夠 pickle,閱讀 pickle 協(xié)議 了解下列特殊方法何時(shí)以及如何被調(diào)用。
序號 | 目的 | 所編寫代碼 | Python 實(shí)際調(diào)用 |
---|---|---|---|
自定義對象的復(fù)制 | copy.copy(x) | x.__copy__() | |
自定義對象的深度復(fù)制 | copy.deepcopy(x) | x.__deepcopy__() | |
在 pickling 之前獲取對象的狀態(tài) | pickle.dump(x, file) | x.__getstate__() | |
序列化某對象 | pickle.dump(x, file) | x.__reduce__() | |
序列化某對象(新 pickling 協(xié)議) | pickle.dump(x, file, protocol_version) | x.__reduce_ex__(protocol_version) | |
* | 控制 unpickling 過程中對象的創(chuàng)建方式 | x = pickle.load(file) | x.__getnewargs__() |
* | 在 unpickling 之后還原對象的狀態(tài) | x = pickle.load(file) | x.__setstate__() |
* 要重建序列化對象,Python 需要?jiǎng)?chuàng)建一個(gè)和被序列化的對象看起來一樣的新對象,然后設(shè)置新對象的所有屬性。__getnewargs__()
方法控制新對象的創(chuàng)建過程,而 __setstate__()
方法控制屬性值的還原方式。
with
語塊中使用的類with
語塊定義了 運(yùn)行時(shí)刻上下文環(huán)境;在執(zhí)行 with
語句時(shí)將“進(jìn)入”該上下文環(huán)境,而執(zhí)行該語塊中的最后一條語句將“退出”該上下文環(huán)境。
序號 | 目的 | 所編寫代碼 | Python 實(shí)際調(diào)用 |
---|---|---|---|
在進(jìn)入 with 語塊時(shí)進(jìn)行一些特別操作 | with x: | x.__enter__() | |
在退出 with 語塊時(shí)進(jìn)行一些特別操作 | with x: | x.__exit__() |
以下是 with file
習(xí)慣用法 的運(yùn)作方式:
# excerpt from io.py: def _checkClosed(self, msg=None): '''Internal: raise an ValueError if file is closed ''' if self.closed: raise ValueError('I/O operation on closed file.' if msg is None else msg) def __enter__(self): '''Context management protocol. Returns self.''' self._checkClosed() ① return self ② def __exit__(self, *args): '''Context management protocol. Calls close()''' self.close() ③
__enter__()
和一個(gè) __exit__()
方法。該 __enter__()
方法檢查文件是否處于打開狀態(tài);如果沒有, _checkClosed()
方法引發(fā)一個(gè)例外。 __enter__()
方法將始終返回 self —— 這是 with
語塊將用于調(diào)用屬性和方法的對象 with
語塊結(jié)束后,文件對象將自動關(guān)閉。怎么做到的?在 __exit__()
方法中調(diào)用了 self.close()
. 該
__exit__()
方法將總是被調(diào)用,哪怕是在with
語塊中引發(fā)了例外。實(shí)際上,如果引發(fā)了例外,該例外信息將會被傳遞給__exit__()
方法。查閱 With 狀態(tài)上下文環(huán)境管理器 了解更多細(xì)節(jié)。
真正神奇的東西
如果知道自己在干什么,你幾乎可以完全控制類是如何比較的、屬性如何定義,以及類的子類是何種類型。
序號 | 目的 | 所編寫代碼 | Python 實(shí)際調(diào)用 |
---|---|---|---|
類構(gòu)造器 | x = MyClass() | x.__new__() | |
* | 類析構(gòu)器 | del x | x.__del__() |
只定義特定集合的某些屬性 | x.__slots__() | ||
自定義散列值 | hash(x) | x.__hash__() | |
獲取某個(gè)屬性的值 | x.color | ||
設(shè)置某個(gè)屬性的值 | x.color = 'PapayaWhip' | ||
刪除某個(gè)屬性 | del x.color | ||
控制某個(gè)對象是否是該對象的實(shí)例 your class | isinstance(x, MyClass) | MyClass.__instancecheck__(x) | |
控制某個(gè)類是否是該類的子類 | issubclass(C, MyClass) | MyClass.__subclasscheck__(C) | |
控制某個(gè)類是否是該抽象基類的子類 | issubclass(C, MyABC) | MyABC.__subclasshook__(C) |