英文原文:http://www.rafekettler.com/magicmethods.html#conclusion
1.__new__ 是靜態方法(__new__ is a static method),執行個體化時以類作對象為第一個參數(The first argument to __new__ must be a class; the remainingarguments are the arguments as seen by the constructor call.)調用__new__返回執行個體對象,然後再調用__init__對執行個體對象做進一步初始化;對於一些不可變對象的類(int,str,tuple),其__init__是無效的,要想在初始化過程中改變其值,需要override
__new__方法。改寫__new__時還是需要調用父類的__new__方法的,只是我們可以在之前改變參數或者在之後改變返回的執行個體對象。改寫的__new__內部調用父類__new__方法時不可把父類作為第一個參數傳入,否則會得到父類的執行個體對象,應該傳入新類(A __new__ method that overrides a base class's __new__ methodmay call that base class's __new__ method. The first argument to thebase
class's __new__ method call should be the class argument to theoverriding __new__ method, not the base class; if you were to pass inthe base class, you would get an instance of the base class)。官網對__new__的描述非常詳細http://www.python.org/download/releases/2.2/descrintro/#__new__。
2.__del__相當於解構函式(對應的,__new__和__init__就是建構函式了),但並不是在del x的時候調用,而是在對象被記憶體回收的時候調用。其作用主要是處理一些額外的清理工作,比如關閉socket和檔案描述符。__del__最好別用,或謹慎使用,因為不能保證__del__在什麼時候執行。最好的習慣是自己手動做必要的善後工作,比如串連不用了就自己去把它關掉,不要交給__del__去做。
3.用於比較的魔法函數,__cmp__,__eq__,__ne__,__lt__,__gt__,__le__,__ge__(__cmp__根據</==/>返回負數/0/整數)
4.__str__由內建函數str()調用,__repr__由內建函數repr()調用(前者主要給人看,後者主要給機器看);__unicode__由內建函數unicode()調用,跟str()類似,只是只是返回的是unicode string。
5.__hash__由內建函數hash()調用,必須返回整數,用於在字典中做快速的key比較;改寫該函數一般也要改寫__eq__,a==b會包含hash(a)==hash(b)。
6.__dir__由內建函數dir()調用,返回一個包含所有屬性的list。一般沒必要改寫,但是在改寫__getattr__/__getattribute__或者動態產生屬性的時候這個函數是很重要的。
7.屬性訪問的控制,從其他語言過來的人抱怨python沒有真正意義上類的封裝(沒法定義私人屬性;且可以隨意的設定和擷取屬性)。事實並非如此,python通過魔法方法實作類別的封裝,而不是顯示的修改類的方法和欄位。
__getattr__(self,name),當訪問不存在的屬性時會調用該方法(不是真正的做類封裝的解決方案)
__setattr__(self,name,value),不管屬性是否已經存在,你可以通過該方法定義/設定一個屬性的行為。也就是說你可以定製自己的規則使其在類的屬性發生改變時發揮作用(確實是一種類的封裝)
__delattr__,跟__setattr__類似,只不過它是刪除屬性,而非設定屬性。調用del self.name時在__delattr__的實現中可能會導致無窮遞迴,需謹慎
__getattribute__(self, name),不推薦使用(用途很少且很難做到沒BUG),只能用於新風格的類(最新版本python裡面都是新風格的類)。當被顯式調用或者__getattribute__返回AttributeError時,__getattr__會被調用
def __setattr__(self, name, value): self.name = value # since every time an attribute is assigned, __setattr__() is called, this # is recursion. # so this really means self.__setattr__('name', value). Since the method # keeps calling itself, the recursion goes on forever causing a crashdef __setattr__(self, name, value): self.__dict__[name] = value # assigning to the dict of names in the class # define custom behavior here class AccessCounter(object): '''A class that contains a value and implements an access counter. The counter increments each time the value is changed.''' def __init__(self, val): super(AccessCounter, self).__setattr__('counter', 0) super(AccessCounter, self).__setattr__('value', val) def __setattr__(self, name, value): if name == 'value': super(AccessCounter, self).__setattr__('counter', self.counter + 1) # Make this unconditional. # If you want to prevent other attributes to be set, raise AttributeError(name) super(AccessCounter, self).__setattr__(name, value) def __delattr__(self, name): if name == 'value': super(AccessCounter, self).__setattr__('counter', self.counter + 1) super(AccessCounter, self).__delattr__(name)]
8.定製序列:自訂不可變容器,只用定義__len__和__getitem__方法;自訂可變容器,需要定義__len__/__getitem__/__setitem__/__delitem__。如果需要具備可迭代的屬性,還要定義__iter__(__iter__返回迭代器,該迭代器必須遵守迭代協議,即,需要迭代器有__iter__和next方法)
__getitem__(self,key),使用self[key]的時候被調用,不可變容器和可變容器都支援該方法
__setitem__(self,key,value),使用self[key]=value的時候被調用,可變容器才支援該方法
__delitem__(self,key),使用del self[key]的時候被調用,可變容器才支援該方法
__iter__(self),返回一個容器的迭代器,有很多方式會觸發該方法調用,常見的是內建函數iter()和“for x in container:”文法。迭代器需要定義方法__inter__返回自身
__reversed__(self),使用內建函數reversed()的時候被調用,返回一個逆序序列,實現該方法需要序列有序(如tuple和list)
__contains__(self,item),使用“in”和“not in”文法的時候觸發該方法調用,如果不定義該方法python會迭代這個序列並在遇到目標(item)時候返回True
__missing__(self,key),為dict的子類使用,當請求的key不存在的時候會調用該方法
9.reflection,很少會用到這些,但是如果需要你會發現python給了你這個功能
__instancecheck__(self,instance),使用isinstance(instance,class)的時候會觸發該函數調用
__subclasscheck__(self,subclass),使用issubclass(subclass,class)的時候會觸發該函數調用
10.callable objects,python中函數也是一種對象
__call__(self, [args...]),該方法允許一個類的執行個體對象可以像函數一樣被調用。x()等價於x.__call__(),參數隨意,但是把執行個體對象當函數調用的時候參數個數必須跟__call__定義時一致;如果在某個class中重複定義了多個__call__方法,最後一個會覆蓋之前的,不論參數形式如何(python中沒有C++裡面那種函數名相同參數形式/傳回值不同的重載)
11.Context Managers,上下文管理器,當對象的建立被with文法封裝時,上下文管理器可以對對象做設定和清理的動作(contextlib模組有類似功能)
__enter__(self),with文法控制的代碼塊執行前會調用被with封裝的對象的__enter__方法,該方法的傳回值被綁定到“with文法的目標”上或者“with文法中as後面的變數”上
__exit__(self,exception_type,exception_value,traceback),with控制的代碼塊執行完畢或終止的時候調用對象的__eixt__方法,該方法用於處理異常、執行清理、或者其他在with代碼塊之後需要做的事情。如果代碼塊執行成功,exception_type、exception_value和traceback是None。否則,你可以選擇處理exception或者丟出去讓使用者處理它:如果你要處理它應該保證處理完以後讓__exit__返回True,如果你不想讓exception被上下文管理器__exit__處理,就什麼也別管讓它去吧
class Closer: '''A context manager to automatically close an object with a close method in a with statement.''' def __init__(self, obj): self.obj = obj def __enter__(self): return self.obj # bound to target def __exit__(self, exception_type, exception_val, trace): try: self.obj.close() except AttributeError: # obj isn't closable print 'Not closable.' return True # exception handled successfully >>> from magicmethods import Closer>>> from ftplib import FTP>>> with Closer(FTP('ftp.somesite.com')) as conn:... conn.dir()...# output omitted for brevity>>> conn.dir()# long AttributeError message, can't use a connection that's closed>>> with Closer(int(5)) as i:... i += 1...Not closable.>>> i6
12.Building Descriptor Objects
__get__(self, instance, owner) __set__(self, instance, value) __delete__(self, instance)
http://onlypython.group.iteye.com/group/wiki/1362-python-39-s-descriptor <descriptor講解>
descriptor到底是什麼呢:簡單的說,descriptor是對象的一個屬性,只不過它存在於類的__dict__中並且有特殊方法__get__(可能還有__set__和__delete)而具有一點特別的功能,為了方便指代這樣的屬性,我們給它起了個名字叫descriptor屬性
data descriptor和non-data descriptor:同時具有__get__和__set__方法,這樣的descriptor叫做data descriptor,如果只有__get__方法,則叫做non-data descriptor
屬性尋找策略,對於obj.attr(注意:obj可以是一個類):
a> 如果attr是一個Python自動產生的屬性(屬性可以分為兩類,一類是Python自動產生的,如__class__,__hash__等,另一類是我們自訂的,如上面的hello,name。類和執行個體對象都有__dict__屬性,裡面存放它們的自訂屬性(對於類,裡面還存放了別的東西)),找到!(優先順序非常高!)
b> 尋找obj.__class__.__dict__,如果attr存在並且是data descriptor,返回data descriptor的__get__方法的結果,如果沒有繼續在obj.__class__的父類以及祖先類中尋找data descriptor
c> 在obj.__dict__中尋找,這一步分兩種情況,第一種情況是obj是一個普通執行個體,找到就直接返回,找不到進行下一步。第二種情況是obj是一個類,依次在obj和它的父類、祖先類的__dict__中尋找,如果找到一個descriptor就返回descriptor的__get__方法的結果,否則直接返回attr。如果沒有找到,進行下一步
d> 在obj.__class__.__dict__中尋找,如果找到了一個descriptor(插一句:這裡的descriptor一定是non-data descriptor,如果它是data descriptor,第二步就找到它了)descriptor的__get__方法的結果。如果找到一個普通屬性,直接返回屬性值。如果沒找到,進行下一步
e> 很不幸,Python終於受不了。在這一步,它raise AttributeError
對屬性賦值時的尋找策略 ,對於obj.attr = value:
a> 尋找obj.__class__.__dict__,如果attr存在並且是一個data descriptor,調用attr的__set__方法,結束。如果不存在,會繼續到obj.__class__的父類和祖先類中尋找,找到 data descriptor則調用其__set__方法。沒找到則進入下一步
b> 直接在obj.__dict__中加入obj.__dict__['attr'] = value
13.Copying
__copy__(self),對某執行個體對象調用copy.copy()時會觸發該執行個體的__copy__方法調用。淺拷貝,執行個體自身是新執行個體,但是它的所有資料都是對原執行個體的資料的引用,修改新執行個體的資料可能會引起原始執行個體的資料變化
__deepcopy__(self, memodict={}),對某執行個體對象調用copy.deepcopy()時會觸發該執行個體的__deepcopy__方法調用。深拷貝,執行個體自身和它的資料都是新的。memodict是之前已拷貝對象的緩衝,當拷貝遞迴資料結構時它可以最佳化拷貝和阻止無盡遞迴。當你要深拷貝一個單獨的屬性時,對該屬性調用copy.deepcopy()並把memodict作為第一個參數(如果要實際使用的話還得深入研究一下~)
14.Pickling Your Objects
Pickling是指對python的資料結構做序列化處理,可以儲存和重新載入一個對象(多用於緩衝)
Pickling不是內建類型。它對於任何遵守pickle協議的類都支援。pickle協議由四個可選方法來定製pickling行為(之前的一篇文章提到了其中兩個方法的使用,http://blog.csdn.net/xiarendeniao/article/details/6774520 item52)
__getinitargs__(self) __getnewargs__(self) __getstate__(self) __setstate__(self, state) __reduce__(self) __reduce_ex__(self)
import timeclass Slate: '''Class to store a string and a changelog, and forget its value when pickled.''' def __init__(self, value): self.value = value self.last_change = time.asctime() self.history = {} def change(self, new_value): # Change the value. Commit last value to history self.history[self.last_change] = self.value self.value = new_value self.last_change = time.asctime() def print_changes(self): print 'Changelog for Slate object:' for k, v in self.history.items(): print '%s\t %s' % (k, v) def __getstate__(self): # Deliberately do not return self.value or self.last_change. # We want to have a "blank slate" when we unpickle. return self.history def __setstate__(self, state): # Make self.history = state and last_change and value undefined self.history = state self.value, self.last_change = None, None
15.哪些文法會觸發魔法方法?