27. 優先使用公有屬性

Python的類有兩種屬性:公有屬性和私有屬性

27. 優先使用公有屬性

任何時候都可以通過句點運算符訪問公有屬性:

27. 優先使用公有屬性

私有屬性是名字以兩個下劃線為前綴的屬性,它們可以通過類的內部方法進行訪問:

27. 優先使用公有屬性

如果在類的外部直接訪問私有屬性會報錯:

27. 優先使用公有屬性

類方法也可以直接訪問類的私有屬性,因為類方法也是定義在類內部的:

27. 優先使用公有屬性

即使子類也不能訪問父類的私有屬性:

27. 優先使用公有屬性

拋出異常:

>>> AttributeError: 'MyChildObject' object has no attribute '_MyChildObject__private_field'

Python解析器在遇到訪問私有屬性時會做一個簡單的轉換。例如上面的方法訪問__private_field時會自動轉換為訪問_MyChildObject__private_field。由於__private_field只定義在了__init__中,所以__private_field的真正名字是_MyParentObject__private_field,所以在子類中訪問__private_field肯定是找不到了。

知道了這個邏輯,你就知道怎麼訪問私有屬性了:

27. 優先使用公有屬性

如果使用對象字典屬性你就能看到這個私有屬性了:

27. 優先使用公有屬性

為什麼私有屬性不能絕對可見呢?最簡單的答案就是:We are all consenting adults here. 就是我們愛怎麼幹就怎麼幹。Python程序員認為,開放的好處大於封閉的壞處(Python programmers believe that the benefits of being open outweigh the downsides of being closed)。

除此之外,Python使得你能夠在任何時候訪問對象的內部,例如訪問對象的屬性(語言的鉤子功能)。如果你這樣做了,那麼Python阻止你訪問對象私有屬性還有什麼價值嗎?

為了減少訪問內部屬性造成破壞,Python程序員應遵循PEP 8的命名規範。以一個下劃線開頭的屬性(例如:_protected_field)是受保護屬性(protected),意味著外部用戶應該小心使用這些屬性。

然而有些新手使用私有屬性來表示子類或者外部對象不能訪問的接口,例如:

27. 優先使用公有屬性

這並不是最好的方式。不可避免的,很多人都會通過構建子類或其它方法來避免現有方法中的缺陷(就像上面的MyClass.get_value,永遠都能訪問私有屬性並返回一個字符串)。通過使用私有屬性,你能讓你的子類重寫和擴展變得困難。而你的子類用戶仍然能通過其它方式訪問私有屬性,例如下面代碼:

27. 優先使用公有屬性

如果類的層級發生改變,那麼上面的代碼就不能工作了。比如給MyClass增加一個新的基類,將原理的私有屬性__value放到新的基類裡面:

27. 優先使用公有屬性

由於私有屬性__value現在被定義在了MyBaseClass裡面了,所以在MyIntegerSubclass訪問self._MyClass__value 將會出錯:

27. 優先使用公有屬性

一般來說,最好允許子類通過受保護的屬性做更多事情。為每一個私有屬性編寫文檔,表明哪個屬性在子類的接口中是可見的,哪個屬性完全不可見。這既是對其他程序員的建議,也是對您未來如何安全地擴展自己的代碼的指導。

27. 優先使用公有屬性

唯一真正需要使用私有屬性的情況是,你擔心這個屬性在子類中可能會產生衝突。

27. 優先使用公有屬性

這種擔心主要發生在公共接口中,由於子類不受你的控制,所以你沒法通過代碼重構來解決問題。這種問題通常都是一個非常通用的命名引起的,就像很多時候都會用到value命名變量一樣。為了減少這種風險的發生,你可以通過在父類中定義私有屬性的方式避免在子類中發生重寫:

27. 優先使用公有屬性


分享到:


相關文章: