python魔法方法-屬性存取控制,python存取控制

來源:互聯網
上載者:User

python魔法方法-屬性存取控制,python存取控制
屬性存取控制

  所謂的屬性存取控制就是控制點號訪問屬性的行為,而且不僅是類的外部,連類的內部也受控制,代碼見真章,邊看代碼邊解釋:

  • __getattr__(self, item)

  定義當訪問不存在的屬性時的行為,注意是不存在的屬性。

class Foo(object):    def __init__(self, value):        self.value = value    def __getattr__(self, item):        print item  # 查看得到的參數是什麼        print type(item)    # 參數的類型是什麼        return 'attr:%s' % item  # 最後返回一個東西看其行為如何a = Foo('scolia')   # 建立一個執行個體

 

測試:

print a.valueprint type(a.value)

其行為和沒定義前正常,下面看看訪問一個不存在的屬性時會發生什麼:

print a.abc

  按照平常的情況,訪問不存在的屬性時肯定會拋出異常,但是這裡輸出了三行,前兩個是方法輸出的,最後一行是外部的print語句輸出的,輸出的是方法的return值。方法得到的是我們訪問的屬性名稱,而且是以字串的形式。

  知道了以上資訊後,我們就可以定製更多:

class Foo(object):    def __init__(self, value):        self.value = value    def __getattr__(self, item):        if item == 'scolia':            return 'can not set attr: %s' % item  # 訪問不存在的scolia屬性時,列印一句話而不報錯        else:            raise AttributeError('not attr name: %s' % item)    # 訪問其他不存在的屬性時,觸發異常。

 

測試:

a = Foo(123)print a.value  # 訪問存在的屬性

  結果正常

print a.scolia  # 訪問不存在的屬性,但我們做了特殊處理的

  沒有觸發異常,和我們設想的一樣。

print a.good  # 訪問不存在的屬性,但應該觸發異常的

  觸發了我們想要的異常。

  這裡要再強調一遍,必須是訪問不存在的屬性時,才會調用這個方法,例如:

a.scolia = 321print a.scolia

  因為這個屬性已經存在了(我們手動添加了),所以訪問它的時候並沒有調用這個方法,而在方法裡所做的任何處理,也不會有效。

更進階的技巧:

class Foo(object):    def __init__(self, value, defulat=None):        self.value = value        self.__defulat = defulat    def __getattr__(self, item):        item = item.lower()  # 用字串的方法對其進行小寫        if item in self.__dict__:            return self.__dict__[item]  # 返回相應的屬性        else:            self.__dict__[item] = self.__defulat    # 若屬性不存在則添加這個屬性並使用預設值            return self.__dict__[item]a = Foo(123)a.scolia = 321print a.SCOliaprint a.good

  我們實現了屬性的不區分大小寫訪問和自動添加不存在的屬性。

  這裡的秘訣在於活用 __dict__ 這個屬性,我在類的屬性中已經討論過這個屬性。這個屬性由python自動建立,是一個字典,包含對象的所有屬性,字典裡的鍵就是屬性名稱,對應的值就是屬性值。所以這裡在這個字典中添加了鍵和值,就相當於為對象添加了屬性和屬性值。

 

 

  • __setattr__(self, key, value)

  定義了設定屬性時的行為,包括在 __init__ 初始化函數中的設定行為:

class Foo(object):    def __init__(self, value, defulat=None):        self.value = value    def __setattr__(self, key, value):        print key, type(key)        print value, type(value)a = Foo('scolia')b = Foo(123)

  這裡可以看到初始化函數中的屬性添加的行為也受到了控制,其中 key 得到的是屬性名稱,以字串的形式;而 value 得到的是屬性值,屬性值根據輸入的不同而不同。

  在這裡,我們僅僅只是列印了幾句話,而沒有進行屬性的添加,所以當我們試圖訪問相應的屬性時,會發現根本就沒有:

print a.value

  觸發了異常,表示沒有相應的屬性。

  知道了這些之後我們可以做很多事情,例如將所有的屬性名稱變成小寫或大寫,控制某些屬性名稱不能添加之類的,就不再舉例。不過,這裡你總不可能用 self.key = value 來添加屬性吧,因為 key 始終是一個字串。這個時候就要使用 __dict__ 屬性了,向這個字典中添加相應的索引值對就可以了,具體就不再示範了。

 

  • __delattr__(self, item)

  定義了刪除一個屬性時的行為,item 得到的也是一個字串形式的屬性名稱,具體細節也無序多說,只要 del 掉 __dict__ 字典中對應是鍵和值就行了。另外,刪除不存在的屬性時調用的也是這個方法。

 

 

  • __getattribute__(self, name)

  這個方法定義了所有屬性訪問的行為,注意是所有,而不是 __getattr__ 中的不存在。當實現了這個方法之後,將會覆蓋 __getattr__ 方法,畢竟所有涵蓋了不存在。

  這個方法只在新式類中有效。

  但是,你也可以顯式的調用 __getattr__,例如 a.__getattr__ 來使用這個被掩蓋的方法,或者是觸發 AttributeError 異常時也會自動調用它。

  然而,非常不建議使用這個方法,因為可能會很多不可預知的異常情況,最常見的就是無盡的遞迴調用,例如:

class Foo(object):    def __init__(self, value):        self.value = value    def __getattribute__(self, item):        return self.__dict__[item]a = Foo('scolia')print a.value

  這段代碼看起來很正常,但是這裡有一個陷阱,因為類中的所有的屬性訪問都是受這幾個魔法方法控制的,包括上面介紹的幾個魔法方法。它們似乎比普通的魔法方法擁有更高的許可權一般。

  但這就導致了一個問題,例如這裡的 self.__dict__[item] ,這句話也受屬性訪問的控制,儘管這個屬性是 python 為我們建立的。

  也就是說擷取 self.__dict__ 時,會再次調用 __getattribute__ 方法,然後方法內又調用了 self.__dict__ 。這樣無限迴圈下去,最終會拋出一個異常。

  異常資訊非常長,這裡我是拉到最後才截的圖。

  其實不僅這個魔法方法會導致這樣異常,上面討論的幾種魔法方法可能都會出現這個問題,只不過這個魔法方法的許可權更大,所以異常出現的可能性更高一些。

  這也就是不推薦這個魔法方法的原因,而使用其他的屬性控制方法的時候也要小心。

  而到目前為止,我們所學到的屬性訪問的方法只有兩種,一是直接用點號訪問,還有就是先通過點號訪問__dict__ 屬性,然後在這個字典中擷取相應的索引值對。而這兩種方法都受到了 __getattribute__ 的控制,調用它們就相當於沒有終點的自調用(有終點的自調用有時能提升效率),那麼這個方法到底要怎麼用呢?

  技巧就是調用父類的這個方法:

class Foo(object):    def __init__(self, value):        self.value = value    def __getattribute__(self, item):        return object.__getattribute__(self, item)  # 非Binder 方法要顯式傳遞selfa = Foo('scolia')print a.value

  這裡調用的是object的這個方法,如果是涉及到繼承的話:

class Boo(Foo):    def __init__(self, value):        self.value = value    def __getattribute__(self, item):        return Foo.__getattribute__(self, item)        # return super(Boo, self).__getattribute__(item)   也可以使用super函數讓python自動在其父類們尋找這個方法。a = Foo('scolia')print a.valueb = Boo(123)print b.value

  訪問正常。

  其實最後調用了還是 object 或其他內建類型的方法。

  而我們姑且不起探究object到底是怎麼實現的,因為這可能是用 C 所寫的,只要會用就可以,雖然這個方法用的也不多。

最後附上一個完整的例子:

class Foo(object):    def __init__(self, value):        self.value = value    def __getattr__(self, item):        if item == 'scolia':            return 'no attr:%s' % item        elif item in self.__dict__:            return self.__dict__[item]        else:            raise AttributeError('no attr:%s' % item)    def __setattr__(self, key, value):        if key == 'good':            print 'can not set the attr: good'        else:            self.__dict__[key] = value    def __delattr__(self, item):        if item == 'a':            print 'no attr: good'        else:            del self.__dict__[item]    def __getattribute__(self, item):        if item == 'a':            raise AttributeError('not a')        return object.__getattribute__(self, item)a = Foo('scolia')print a.value   # 正常訪問a.a = 123   # __getattribute__會觸發AttributeError異常,此時調用__getattr__            # 而__getattr__添加了這個屬性,所以最後異常沒有觸發,屬性也添加了print a.a   # 結果能夠訪問del a.a  # 試圖刪除這個屬性print a.a  # 刪除行為被阻止了,所以該屬性還在a.good = 'good'  # 因為添加被阻止了print a.good  # 所以訪問失敗了

 

結果:

 

  歡迎大家交流。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.