Python 對象 中以雙下划線開頭和結尾的屬性稱為特殊屬性,由於對象的方法也屬於屬性,因此以雙下划線開頭和結尾的方法稱為特殊方法。對這些對象執行一些特定的運算時,Python會自動視圖調用這些實例的特殊方法,從而在Python中可以很輕易地實現運算符的重載。
Python對象常見的特殊屬性
1. C.__name__
對象的名稱,比如類型(type, class)對象的名稱就是系統內置的或自定義的名稱字符串,類型的實例通常沒有屬性 __name__。
>>> class A(object): ... pass ... >>> a = A() >>> A.__name__ 'A' >>> a.__name__ Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'A' object has no attribute '__name__' >>> list.__name__ 'list'
2. C.__bases__
元組,包含 類型對象(type, class) C 的全部基類,類型的實例通常沒有屬性 __bases__。
>>> class A(object): pass >>> class B(A): pass >>> class C(A): pass >>> class D(B, C): pass >>> D.__bases__ (<class '__main__.B'>, <class '__main__.C'>)
3. x.__class__
Python對象,是實例或類型 x 所屬的類型,其值等於 type(x)
>>> d = D() >>> d.__class__ <class '__main__.D'> >>> D.__class__ <type 'type'>
4. x.__dict__
字典,除了一些特殊的屬性,實例、類型等對象的所有屬性,都放置在其 __dict__ 字典中。
Python中有一些通用的特殊函數
1. 初始化與終止化
__new__(cls[, args...])
__new__()是一個靜態方法,用於根據類型創建實例。Python在調用__new__()方法獲得實例后,會調用這個實例的__init__()方法,然后將最初傳給__new__()方法的參數都傳給__init__()方法。
__init__()
__init__()是一個實例方法,用來在實例創建完成后進行必要的初始化,該方法必須返回None.
Python不會自動調用父類的__init__()方法,這需要額外的調用:
super(C, self).__init__()
來完成。
__del__(self)
在GC之前,Python會調用這個對象的__del__()方法完成一些終止化工作。如果沒有__del__()方法,那么Python不做特殊的處理;
Python無視__del__()方法的返回值;
Python不會自動調用父類的__del__()方法,除非顯式調用;
定義了__del__()方法的實例無法參與到循環GC中,所以對於這樣的實例應該避免循環引用;
try/finally語句或with語句可能是比__del__()更好的方式。
2. 表現形式
__repr__(self)
Python內置的repr()函數,`x`表達式形式,或者交互式解釋器在顯示一個表達式語句的結果時,都調用這個對象的__repr__()方法;
__repr__()方法返回的字符串主要是面向解釋器的,改寫的話應該滿足: eval(repr(x)) == x 。
如果沒有定義__repr__(),那么Python使用一種默認的表現形式。
__str__(self)
Python內置的1. str()函數,2. print(x)語句,都會調用對象的__str__()方法;
與__repr__()返回的詳盡的、准確的、無歧義的對象描述字符串不同,__str__()方法只是返回一個對應對象的簡潔的字符串表達形式;
當__str__()缺失時,Python會調用__repr__()方法;
__str__()返回的字符串應該是面向用戶的,可讀的。
__unicode__(self)
Python內置的unicode(x)方法會調用__unicode__()方法;
該方法如果定義,優先級高於__str__()方法;
同時定義這兩個方法的實例,調用它們的結果應該相同。
3. 比較、哈希與布爾值
__lt__(self, other)
x<y 運算將會調用實例x的__lt__(self, other)方法;
__le__(self, other)
x<=y 運算將會調用實例x的__le__(self, other)方法;
__gt__(self, other)
x>y 運算將會調用實例x的__gt__(self, other)方法;
__ge__(self, other)
x>=y 運算將會調用實例x的__ge__(self, other)方法;
__eq__(self, other)
x==y 運算將會調用實例x的__eq__(self, other)方法;
__ne__(self, other)
x!=y 運算將會調用實例x的__ne__(self, other)方法;
*上述用於實例間比較的特殊方法應該返回True或False,或者返回NotImplemented來告訴Python解釋器使用其他的方式進行比較。
__cmp__(self, other)
對於上面提到的比較操作,如果對應的特殊方法沒有定義或者返回NotImplemented,則會調用__cmp__(self, other)再進行一次嘗試;
一些內置的方法:cmp(x, y), max(x, y)或者列表對象的sort()方法也會調用__cmp__()方法;
實現x.__cmp__()方法時,如果x小於y,應該返回-1,如果x大於y,應該返回1;如果x等於y,應該返回0.
對於序列化比較(<, <=, >=, >),如果最終__cmp__()也沒有定義,那么會拋出異常;
對於相等與否的比較(==, !=),如果最終__cmp__()也沒有定義,將會變成身份檢驗:判斷id(x) == id(y)是否成立。
__hash__(self)
三種情形會調用__hash__()方法:1. 內置的hash()方法,2.作為字典的鍵時,3.作為集合的成員時;
__hash__()方法應該返回一個32位長的整數,對與同一個對象,__hash__()方法應該總是返回相同的值;對於 x == y ,即使二者不屬於相同的類型,只要他們是可哈希的(hashable),都應該確保得到 hash(x) == hash(y) ;
沒有 __hash__() 方法,也沒有 __cmp__() 和 __eq__() 方法,上面提到的三種情形將使用id(x)作為替代;
沒有 __hash__() 方法,但是有 __cmp__() 和 __eq__() 方法,上面提到的前兩種方法會拋出異常;
通常只為同時定義了 __cmp__()和/或__eq__() 方法的不可變(immutable)對象定義__hash__()方法,
__nonzero__(self)
判斷一個對象是為真還是假時,例如調用bool(x)方法時,Python會調用x.__nonzero__(self)方法,__nonzero__()方法應該返回True或False。
如果實例沒有__nonzero__()方法,那么Python會調用實例的__len__()方法,當__len__()方法返回0時,Python認為該對象為假。所以如果實例沒有__nonzero__()方法與__len__()方法,則Python認為該實例總是真的;
*所以以一個容器是否非空為判斷條件時,應該寫成:
if container: pass
而不是:
if len(container) > 0 : pass
因為后者將錯過__nonzero__()方法的檢驗。
4. 屬性的引用、綁定與解綁定
__getattribute__(self, name)
訪問對象的屬性 x.y 時,Python會自動調用 x.__getattribute__('y') 方法;
__getattribute__() 方法應該返回被訪問的屬性或者是拋出異常 AttributeError ;
重寫類型的 __getattribute__() 方法會導致實例的屬性訪問變慢。
__getattr__(self, name)
當常規的屬性訪問( x.__class__ 或 x.__dict__ 的鍵訪問)無法找到目標屬性時,Python會調用 __getattr__() 方法;
如果該方法沒能找到目標屬性,應該拋出 AttributeError 。
*區別 __getattribute__ 和 __getattr__,前者是任何通過 x.y 訪問實例的屬性時都會調用的特殊方法,而后者則是在正常訪問形式下無法找到的情況下才會被調用。
__setattr__(self, name, value)
綁定實例的某個屬性(賦值),例如 x.y = value 時,Python會自動調用 x.__setattr__('y', value) 方法;
Python無視 __setattr__() 方法的返回值;
如果沒有定義 __setattr__() 方法,Python將賦值 x.y = value 解釋成 x.__dict__['y'] = value 。
__delattr__(self, name)
當解綁定一個對象的某個屬性(例如調用 del x.y )時,會調用 x.__delattr__('y') 方法;
Python無視__delattr__()方法的返回值;
如果沒有定義__delattr__()方法,那么Python將 del x.y 解釋成 del x.__dict__['y'] 。
5. 可調用對象
__call__(self[, args...])
定義了該方法的對象可以像函數那樣被調用,因此被稱為可調用對象。
二 、容器的特殊方法
容器可以是 序列(sequence) 或者 映射(mapping)
__contains__(self, item)
布爾測試 y in x 會調用 x.__contains__(y) ;
對於序列x而言,如果y等於x中的某一個值,那么__contains__()方法應該返回True;
對於映射x而言,如果y等於x的鍵中的某一個,那么__contains__()方法應該返回True;
如果沒有定義__contains__()方法,那么測試 y in x 等價於:
for z in x: if y == z: return True else: return False
__delitem__(self, key)
從容器中解綁一個項或者切片(如 del x[key] )將會調用 x.__delitem__(key) 方法,
只有可變對象才應該定義這個方法。
__getitem__(self, key)
調用 x[key] (索引或切片)時將會調用 x.__getitem__(key) 方法;
__iter__(self)
對於試圖遍歷一個容器的全部元素的請求(例如 for i in x ),Python將會調用 x.__iter__() 來獲得x上的一個迭代器;
Python內置函數 iter(x) 也會調用 x.__iter__() 方法;
如果沒有定義__iter__()方法,那么iter(x)方法會合成並返回一個包含x的新的迭代器,然后逐個返回x中的元素;
最好在每個容器中都實現__iter__()方法。
__len__(self)
Python內置的 len(x) 調用或其他試圖得知x中的元素個數的函數都會最終調用 x.__len__() ;
__len__()方法應該返回整形的數值;
沒有定義__nonzero__()時,Python也會調用__len__()方法來判斷容器的真假;
容器都應該定義__len__()方法,除非實現起來特別昂貴的情形。
__setitem__(self, key, value)
對容器的一個元素或切片進行綁定(如: x[key] = value )時,Python會調用 x.__setitem__(key, value) ;
可變(mutable)容器才應該定義該方法。
內置object類型的特殊方法
object 類型是所有內置和自定義類型的祖先,它定義了一些基本的特殊方法,這些方法的含義在前文已經介紹過:
__new__
__init__
__delattr__
__getattribute__
__setattr__
__hash__
__repr__
__str__
靜態方法(Static Method)
靜態方法是指:
- 盡管定義和使用上和普通的函數完全相同,但通常要在某個類型中定義,;
- 通過 類型 和 實例 都能調用;
- 定義靜態方法時不用額外指定第一個參數,Python也不會隱式地傳遞任何內容作為第一個參數;
- 使用裝飾器 @staticmethod 來定義靜態方法;
例如:
>>> class A(object): ... @staticmethod ... def f(x, y): return x + y ... >>> A.f(1, 9) 10 >>> a = A() >>> a.f(1,9) 10
靜態方法最大的特點就是不需要額外的參數。
類方法(Class Method)
類方法是指:
- 定義時的第一個參數為一個類型 cls ,調用類方法時Python自動地將調用該方法的類型(或者調用該方法的實例所屬的類型)傳遞給類方法的首個參數;
- 通過 類型 和 實例 都能夠調用;
- 使用裝飾器 @classmethod 定義類方法
例如:
>>> class A(object): ... @classmethod ... def f(cls, x): ... print 'class name: %s.' % cls.__name__ ... print 'the argument you passed in: %s.' %x ... >>> A.f('hello') class name: A. the argument you passed in: hello. >>> a = A() >>> a.f('world') class name: A. the argument you passed in: world.
類方法的最大特點是定義時的第一個參數代表調用該方法的類型或實例所屬的類型,而Python會隱式地將這個類型對象傳給類方法。
靜態方法和類方法都是類級方法,不同於實例方法。