Python中的 @property特性
你將了解Python中的 @property特性, 以pythonic的方式使用getter和setter。
目錄表
從一個例子開始
使用 Getter 和 Setter
@property的力量
深入理解Property
Python有一個很棒的概念,叫做property,它使面向對象的程序員的生活更加簡單。
在定義和研究@property是什麼之前,讓我們先直觀感受下為什麼首先應該使用它。
從一個例子開始
我們假設你決定創建一個可以以攝氏度存儲溫度的類[1],它還將實現一個將溫度轉換為華氏度的方法。一種方法是像下面這樣做。
我們可以使用這個類創建對象,並按照我們的意願操作其屬性
temperature
。你可以在Python shell上試試這些操作。在轉換成華氏溫度時,由於浮點運算錯誤(請在Python解釋器中嘗試
1.1 + 2.2
),小數點多了一位。當我們賦值或檢索任何對象屬性像
temperature
時,如上面所示,Python都會在該對象的__dict__
字典中搜索它。因此,
man.temperature
在內部會變成man.__dict__["temperature"]
。現在,讓我們進一步假設我們的類在客戶中很受歡迎,他們開始在自己的程序中使用它。他們對這個對象做了各種各樣的賦值操作。
有一天,一個值得信賴的客戶來找我們,建議溫度不能低於-273攝氏度(熱力學專業的學生可能會說實際上是-273.15攝氏度),也被稱為絕對零度。他還要求我們實現這個值約束。作為一家追求客戶滿意的公司,我們很高興地採納了這個建議,並發布了1.01版本(對現有的類進行升級)。
使用Getter和Setter
上述對值進行約束的一個明顯解決方案是隱藏屬性
temperature
(使其私有)並定義新的 getter 和 setter 介面來操作它。可以按照下面這樣做。從上面可以看出,我們定義了
get_temperature()
和set_temperature()
兩個新方法,並且用_temperature
替換了temperature
。在Python中,開頭的下劃線 (_) 用於表示私有變數。這次更新成功地實現了新的限制。我們沒法再將溫度設置在-273度以下。
請注意,Python中不存在私有變數,但還是有一些簡單的準則可以遵循。Python語言本身並不會做限制。
但這並不是什麼大問題。上述更新的一個大問題是,所有在其程序中實現我們前面的類的客戶都必須修改他們的代碼,將
obj.temperature
修改為
obj.get_temperature()
,並且將像obj.temperature = val
的所有賦值語句修改為obj.set_temperature(val)
。這種重構可能會給客戶帶來數十多萬行代碼的麻煩。
總之,我們的新更新不向後兼容。這時property就派上用場了。
@property的力量
處理上述問題的Pythonic的方法是使用property。以下是我們如何實現它。
然後,在shell中運行以下代碼並注意觀察。
我們在
get_temperature()
和set_temperature()
中添加了一個print()
函數,來清楚地觀察它們是否正在執行。代碼的最後一行創建了一個property對象
temperature
。簡單地說,property將一些代碼(get_temperature
和set_temperature
)附加到成員屬性訪問中(temperature
)。任何檢索
temperature
值的代碼都將自動調用get_temperature()
,而不是使用字典(__dict__)進行查找。類似地,任何對temperature
賦值的代碼都會自動調用set_temperature()
。這是Python中一個很酷的特性。我們可以看到上面的
set_temperature()
即使在創建對象時也被調用。你能猜到這是為什麼嗎?
原因是在創建對象時,
__init__()
方法被調用。這個方法有self.temperature = temperature
這行代碼,所以賦值時會自動調用set_temperature()
。類似地,任何訪問,比如
c.temperature
,都會自動調用get_temperature()
。這就是property的作用。這裡有更多的幾個例子。通過使用 property,可以看到,我們修改了類並實現了對值的約束,而不需要對客戶代碼進行任何更改。因此,我們的實現是向後兼容的,皆大歡喜。
最後請注意,實際的溫度值存儲在私有變數
_temperature
中。屬性temperature
是一個property對象,它為這個私有變數提供介面。深入理解 Property
在Python中,
property()
是一個內置函數,用於創建和返回一個property對象。這個函數的簽名是:其中,
fget
是獲取屬性值的函數,fset
是設置屬性值的函數,fdel
是刪除屬性的函數,doc
是字元串(像注釋一樣)。從上述實現中可以看出,這些函數參數是可選的。因此,可以按照以下方式創建property對象。property對象有三個方法,
getter()
、setter()
和deleter()
,用於後續指定fget
、fset
和fdel
。這意味著本行代碼可以分解為
這兩段代碼是等價的。
熟悉
Python中的裝飾器[2]
的程序員可能會意識到,上面的構造可以實現為裝飾器。我們可以繼續,不去定義
get_temperature
和set_temperature
名稱,因為它們是不必要的,並且會污染類命名空間。為此,我們在定義getter
和setter
函數時重用了名稱temperature
。這就是它實現的方式。上述實現是既簡單又推薦使用的創建property的方法。當你在Python中尋找property時,你很可能會遇到這些類型的構造。
好了,今天就到這裡。
相關鏈接:
[1]——
https://www.programiz.com/python-programming/class
[2]——
https://www.programiz.com/python-programming/decorator
英文原文:https://www.programiz.com/python-programming/property
譯者:野生大熊貓
TAG:Python程序員 |