總的來講python的 magic method 主要是圍繞一些類中形如 __xx__ 的樣子的方法。python
1 構造對象和初始化對象 __new__, __init__ 等app
2 控制屬性訪問 __getattribute__, __setattr__ 等ide
3 建立對象描述符 __get__, __set__, __del__函數
4 可調用對象 __call__測試
5 上下文管理 __enter__, __exit__spa
6 自定義容器 __getitem__, __iter__ 等.net
7 反射 __instancecheck__ 等code
8 迭代器 __iter__, __next__對象
1.1 __new__ 用來建立類並返回這個類的實例(建立)。
1.2 __init__ 將傳入的參數來初始化該實例(初始化)。
1.3 __del__ 在對象生命週期結束的時候,該方法會被調用。
__new__ 與 __init__ 的示例
class B(object): def __init__(self): print "init" def __new__(cls, *args, **kwargs): # 必定要寫傳入cls,由於要實例化類 print "new %s" % cls return object.__new__(cls, *args, **kwargs) b = B() [out:] new <class '__main__.B'> init
class A(object): pass class B(A): def __init__(self): print "init" def __new__(cls, *args, **kwargs): print "new %s" % cls # 當實例化父類的時候,本類的init將不會被調用 return object.__new__(A, *args, **kwargs) b = B() print type(b) [out:] new <class '__main__.B'> <class '__main__.A'>
1.4 __str__函數用於處理打印實例自己的時候的輸出內容, 若是覆寫該函數,則默認輸出函數名稱和內存地址
class Foo(object): def __init__(self): self.name = 'wwww' def __str__(self): return self.name foo = Foo() print foo # out: # wwww # 若是沒有__str__則打印 <__main__.Foo object at 0x000000000253DA90>
2.1 __getattr__(self, item) 當獲取一個不存在的字段時候會被調用。你也能夠定義調用時候的處理方式 以下:
class Desc(object): def __init__(self, name): self.name = name def __getattr__(self, item): if item == 'mmc': return 'dunk' return 'nothing'
2.2 __setattr__(self, key, value) 關於__setattr__有兩點須要說明:
第一,使用它時必須當心,不能寫成相似self.name = 「Tom」這樣的形式,由於這樣的賦值語句會調用__setattr__方法,這樣會讓其陷入無限遞歸;
第二,你必須區分 對象屬性 和 類屬性 這兩個概念。
實例對象的__setattr__方法能夠定義屬性的賦值行爲,無論屬性是否存在。當屬性存在時,它會改變其值;當屬性不存在時,它會添加一個對象屬性信息到對象的__dict__中,然而這並不改變類的屬性。
class Foo(object): def __init__(self, name): self.name = name def __getattr__(self, item): return 'is nothing' def __setattr__(self, key, value): return super(Foo, self).__setattr__(key, value) f = Foo('wwe') print f.name print f.ufc f.ufc = 123 print f.ufc [out:] wwe is nothing
2.3 __delattr__ 刪除一個屬性
和__setattr__方法要注意無限遞歸的問題,重寫該方法時不要有相似del self.name的寫法。
class Desc(object): def __init__(self, name): self.name = name def __getattr__(self, item): return 'nothing' def __delattr__(self, item): print 'process del' return super(Desc, self).__delattr__(item) de = Desc('zhww') print de.name del de.name print de.name [out:] zhww process del nothing
2.4 __getattribute__(self, item) 當訪問字段的時候會被無條件調用。訪問字段首先調用 __getattribute__ 方法,若是該方法未能處理字段才調用 __getattr__方法 ,
注意咱們使用了super()方法來避免無限循環問題。 以下:
class Desc(object): def __init__(self, name): self.name = name def __getattr__(self, item): return 'nothing' def __getattribute__(self, item): try: print 'hahha' return super(Desc, self).__getattribute__(item) except AttributeError: return 'default' desc = Desc('wwt') print desc.name print desc.wwt [out:] hahha #每次調用都會訪問 __getattribute__ 方法 wwt hahha default # 若是 __getattribute__ 能夠處理, 就不會調用__getattr__ 方法
我認爲若是說 __getattribute__, __getattr__, __setattr__, __delattr__ 等方法用來實現對象中屬性查找、設置、刪除的邏輯。
那麼描述符就是把某個具體屬性當成對象進行查找,設置,刪除的邏輯(很重要)
3.1 爲了成爲一個描述符,一個類必須至少有__get__,__set__,__delete__方法被實現:
__get__(self, instance, owner): 定義了當描述器的值被取得的時候的行爲。instance是擁有該描述器對象的一個實例(obj)。owner是擁有者自己(cls)。
__set__(self, instance, value): 定義了當描述器的值被改變的時候的行爲。instance是擁有該描述器類的一個實例。value是要設置的值。
__del__(self, instance): 定義了當描述器的值被刪除的時候的行爲。instance是擁有該描述器對象的一個實例。
3.2 數據描述符與非數據描述符
非數據描述符:只定義了__get__ 屬性
數據描述符:定義了__get__, __set__, __delete__ 屬性
class Describer(object): def __init__(self): self._func = None # 用__call__實現類的裝飾器 def __call__(self, func): print 'im Describer call' print func self._func = func return self def __get__(self, instance, owner): self._func(instance) instance.foo2() return 123 class Foo(object): def __init__(self): pass @Describer() def foo(self): print 'im Foo instance foo' def foo2(self): print 'im Foo instance foo2' f = Foo() # 注意,由於使用了描述符,因此類方法會被封裝在描述符中,而這個方法會被描述符做爲屬性返回 # 因此foo做爲描述符的屬性方法存在 print f.foo
''' out: im Describer call <function foo at 0x0000000002756668> im Foo instance foo im Foo instance foo2 '''
3.3 注意
3.3.1 方法會變成屬性的調用方式
注意,由於使用了描述符,因此類方法會被封裝在描述符中,而這個方法會被描述符做爲屬性返回
因此foo做爲描述符的屬性方法存在
3.3.2 描述符若是在做爲一個類的屬性,必定要是類屬性, 實例中是不容許使用描述符的。
class Describer(object): def __init__(self): self.name = 'www' def __get__(self, instance, owner): print 'im __get__' return self.name class Foo(object): def __init__(self): self.des = Describer() foo = Foo() print Foo.__dict__ foo.des # 並無返回值 # out: # {'__dict__': <attribute '__dict__' of 'Foo' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, '__init__': <function __init__ at 0x00000000025F65F8>}
由於調用 foo.des 時刻,首先會去調用Foo(即Owner)的 __getattribute__() 方法,該方法將 foo.des 轉化爲Foo.__dict__['des'].__get__(t, Foo), 可是呢,實際上 Foo並無 des 這個屬性,des 是屬於實例對象的,因此,只能忽略了。
4.1 __call__(self, [args...]) 容許一個類的實例像函數同樣被調用。實質上說,這意味着 x() 與 x.__call__() 是相同的。這會讓類的實例在實現方法上很是優美
5.1 __enter__(): 在使用with語句時調用,會話管理器在代碼塊開始前調用,返回值與as後的參數綁定
5.2 __exit__(): 會話管理器在代碼塊執行完成好後調用,在with語句完成時,對象銷燬以前調用
首先,實現不可變容器的話,你只能定義 __len__ 和 __getitem__ (下面會講更多)。可變容器協議則須要全部不可變容器的全部,另外還須要 __setitem__ 和 __delitem__。若是你但願你的對象是可迭代的話,你須要定義 __iter__ 會返回一個迭代器。迭代器必須遵循迭代器協議,須要有 __iter__(返回它自己) 和 next。 ---- j_hao104
6.1 _len__(self) 返回容器的長度。對於可變和不可變容器的協議,這都是其中的一部分。
6.2 __getitem__(self, item) 定義當某一項被訪問時,使用self[key]所產生的行爲。這也是不可變容器和可變容器協議的一部分。若是鍵的類型錯誤將產生TypeError;若是key沒有合適的值則產生KeyError。
6.3 __setitem__(self, key, value) 當你執行self[key] = value時,調用的是該方法。
6.4 __delitem__(self, key) 定義當一個項目被刪除時的行爲(好比 del self[key])。這只是可變容器協議中的一部分。當使用一個無效的鍵時應該拋出適當的異常。
6.5 __iter__(self) 返回一個容器迭代器,不少狀況下會返回迭代器,尤爲是當內置的iter()方法被調用的時候,以及當使用for x in container:方式循環的時候。迭代器是它們自己的對象,它們必須定義返回self的__iter__方法。
6.6 __reversed__(self) 實現當reversed()被調用時的行爲。應該返回序列反轉後的版本。僅當序列能夠是有序的時候實現它,例如對於列表或者元組。
6.7 __contains__(self, item) 定義了調用in和not in來測試成員是否存在的時候所產生的行爲。你可能會問爲何這個不是序列協議的一部分?由於當__contains__沒有被定義的時候,若是沒有定義,那麼Python會迭代容器中的元素來一個一個比較,從而決定返回True或者False。
6.8 __missing__(self, key) dict字典類型會有該方法,因此必定要是dict類的子類,它定義了key若是在容器中找不到時觸發的行爲。好比d = {‘a’: 1}, 當你執行d[notexist]時,d.__missing__[‘notexist’]就會被調用。但注意它必定是在__getitem__(self, item)處理後執行,因此通常將__missing__(self, key)定義在__getitem__() 異常處理中。
下面是自定義一個MyDict()類
#!/usr/bin/python # -*- coding: utf-8 -*- class MyDict(dict): def __init__(self): self.kind = {} def __len__(self): return len(self.kind) def __getitem__(self, item): try: return self.kind[item] except KeyError: return self.__missing__(item) # 首先,__getitem__()方法須要在訪問鍵失敗時,調用__missing__()方法,而不是item不存在時直接調用 missing def __missing__(self, key): return 'There is not item.' def __setitem__(self, key, value): self.kind[key] = value def __delitem__(self, key): del self.kind[key] def __iter__(self): return iter(self.kind) def __reversed__(self): return reversed(self.kind) # 但類型必須是有序類型,本例中的dict屬於無序,因此沒法調用reversed方法 def __contains__(self, item): if item in self.kind: print 'MyTrue' return True else: print 'MyFalse' return False md = MyDict() md['name'] = 'wwt' md['age'] = 23 md['del'] = 'nothing' print type(md) print len(md) print del md['del'] for item in md: print item print print md['haha'] print print 'name' in md [out:] <class '__main__.MyDict'> age name There is not item. MyTrue True
7.1 __instancecheck__(self, instance) 檢查一個實例是否是你定義的類的實例
7.2 __subclasscheck__(self, subclass) 檢查一個類是否是你定義的類的子類
這兩個方法本身沒試過,試過夠再細說。
isinstance(mc, MyClass)
issubclass(SubClass, MyClass) 這倆個個方法能夠實現一樣功能
8.1 __iter__(self) 通常 return self 自己,也就是說返回對象自身
8.2 __next__(self) 在每次調用 __next__ 方法 或 調用 next 方法時, 會執行該函數
仿寫內置 的range方法
class MyRange(list): def __init__(self): super(MyRange, self).__init__() def __iter__(self): return self def __next__(self): if self._i: self._s += self._i else: self._s += 1 return def __call__(self, s, t, interval=None): self._s = s self._t = t self._i = interval while self._s <= self._t: self.append(self._s) self.__next__() return self mr = MyRange() print mr(0, 5, 2) # [0, 2, 4] print range(0, 4) # [0, 2, 4]
參考文獻:
my.oschina.net/jhao104/blog/779743