python magic_method

總的來講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 構造對象和初始化對象 blog

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 控制屬性訪問  

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__ 方法

 

3 建立對象描述符  

我認爲若是說 __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 可調用的對象  

4.1 __call__(self, [args...]) 容許一個類的實例像函數同樣被調用。實質上說,這意味着 x() 與 x.__call__() 是相同的。這會讓類的實例在實現方法上很是優美

 

5 上下文管理  

5.1 __enter__(): 在使用with語句時調用,會話管理器在代碼塊開始前調用,返回值與as後的參數綁定

5.2 __exit__(): 會話管理器在代碼塊執行完成好後調用,在with語句完成時,對象銷燬以前調用

 

6 建立自定義容器  

首先,實現不可變容器的話,你只能定義 __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 反射  

7.1 __instancecheck__(self, instance) 檢查一個實例是否是你定義的類的實例

7.2 __subclasscheck__(self, subclass) 檢查一個類是否是你定義的類的子類

這兩個方法本身沒試過,試過夠再細說。

isinstance(mc, MyClass)
issubclass(SubClass, MyClass) 這倆個個方法能夠實現一樣功能

 

8 迭代器 

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]
View Code

 

 

 

 

參考文獻

my.oschina.net/jhao104/blog/779743

相關文章
相關標籤/搜索