python 元類編程

##基礎知識:## Python Class 對象或類型經過內置成員 dict 來存儲成員信息。
還能夠經過重載 getattrsetattr 來攔截對成員的訪問,須要注意的是 getattr 只有在訪問不存在的成員時纔會被調用。
若是類型繼承自 object,咱們可使用 getattribute 來攔截全部(包括不存在的成員)的獲取操做html

dict 返回類的屬性字典, 會調用__getattrbute__
若是有一個類A;
a = A()
a.dict 和 A.__dict__會有所不一樣, a__dict__中沒有方法,而 A.dict 有方法。
因此可見 類方法是類的屬性被全部對像共享。python

python的對象能夠動態加屬性,例如  a.ss=3, 這時a.__dict__就會有ss的key, 而A.__dict__ 不會有這個key,從而引伸出類屬性和對象屬性(這裏不作過多解釋)。

這裏要問一個問題了:
對象的__dict__中沒有方法, 那對象是怎麼調用方法的呢?
在這裏就引伸出Descriptor個東西:git

class RevealAccess(object):
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        print 'Retrieving', self.name
        return self.val

    def __set__(self, obj, val):
        print 'Updating' , self.name
        self.val = val

在類中定義__get__和__set__方法的,成爲descriptor
它是用來作什麼的呢。。。。。
再寫一個類:github

class MyClass(object):
    x = RevealAccess(10, 'var "x"')

    def __init__(self):
        self.y = 5

    def test(self):
        print 'hello world!'
    test = RevealAccess(test, 'var "x"')

if __name__ == "__main__":
    m = MyClass()
    print m.y
    # 輸出
    # 5
    print m.x     
    # 輸出
    # Retrieving var "x"
    # 10
    print m.test
    # 輸出
    # Retrieving var "x"
    # <function test at 0x02CBE870>

哈哈, 結果看出來了吧;當對像調用x(屬性),test(方法)的時候都調用了__get__方法。 這個RevealAccess的對象就是一個descriptor。訪問屬性m.x就是調用__get__方法,設置屬性值就是調用__set__方法。還能夠有一個__delete__方法,在del m.x時被調用。函數

下一個問題:
print MyClass.dict["test"] # 輸出: <function test at 0x02C9E8F0> print m.test # 輸出: <bound method MyClass.test of <main.MyClass object at 0x02C9DB70>> 這是什麼狀況, 這倆明顯不是一個東西, 什麼狀況。
這說明test方法在類中僅僅是一個function, 他跟對象仍是不要緊。 print MyClass.dict["test"].get(m, Myclass) # 輸出: <bound method MyClass.test of <main.MyClass object at 0x02CDDB90>> 這回是一個東西了,其實,類的成員函數就是一個descriptor,在實例化對象m的時候,作了m.test = MyClass.dict["test"].get(m, Myclass) 這麼一件事,這回通了。哈哈。post

有了這個基礎: 理解一下staticmethod和classmethod這兩個decorator,staticmethod就是像RevealAccess同樣忽略第一個參數,直接返回參數。classmethod就是手動賽一個對像進去: def get(self, obj, klass=None): if klass is None: klass = type(obj) def newfunc(*args): return self.f(klass, *args) return newfunccode

dict 妙用
<!-- lang: python --> class Person: def init(self,_obj): self.name = _obj['name'] self.age = _obj['age'] self.energy = _obj['energy'] .............. class Person: def init(self, _obj): self.dict.update(_obj) 這樣就減小了不少代碼量
delattr: 當使用 p = Person(); del p.name 會調用htm

主要參考http://hbprotoss.github.io/posts/python-descriptor.html http://blog.jobbole.com/21351/對象

相關文章
相關標籤/搜索