Python有一個(gè)偉大的概念,稱為屬性,它使面向?qū)ο蟮某绦騿T的生活變得更加簡(jiǎn)單。
在定義和詳細(xì)了解@property是什么之前,讓我們了解為什么首先需要使用它。
假設(shè)您決定創(chuàng)建一個(gè)以攝氏度為單位存儲(chǔ)溫度的類。它還將實(shí)現(xiàn)一種將溫度轉(zhuǎn)換為華氏溫度的方法。其中一種方法如下。
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32
我們可以從此類中制造出對(duì)象并根據(jù)需要操縱屬性temperature 。在Python shell上嘗試這些。
>>> # 創(chuàng)建新對(duì)象 >>> man = Celsius() >>> # 設(shè)定溫度 >>> man.temperature = 37 >>> # 得到溫度 >>> man.temperature 37 >>> # 獲得華氏度 >>> man.to_fahrenheit() 98.60000000000001
轉(zhuǎn)換為華氏溫度時(shí),多余的小數(shù)位是由于浮點(diǎn)運(yùn)算錯(cuò)誤(在Python解釋器中嘗試1.1 + 2.2)。
如上所示,每當(dāng)我們分配或檢索任何對(duì)象屬性(如temperature)時(shí),Python都會(huì)在對(duì)象的__dict__字典中進(jìn)行搜索。
>>> man.__dict__ {'temperature': 37}
因此,man.temperature內(nèi)部變?yōu)閙an.__dict__['temperature']。
現(xiàn)在,讓我們進(jìn)一步假設(shè)我們的課程在客戶中很受歡迎,并且他們開始在程序中使用它。 他們對(duì)對(duì)象進(jìn)行了各種分配。
有一天,一個(gè)值得信賴的客戶來找我們,建議溫度不能低于-273攝氏度(熱力學(xué)專業(yè)的學(xué)生可能會(huì)說實(shí)際上是-273.15攝氏度),也被稱為絕對(duì)零度。他進(jìn)一步要求我們實(shí)現(xiàn)這個(gè)值約束。作為一家追求客戶滿意度的公司,我們很高興地聽取了這個(gè)建議,并發(fā)布了1.01版本(對(duì)現(xiàn)有類的升級(jí))。
解決上述約束的一個(gè)明顯方法是隱藏屬性temperature(將其設(shè)為私有),并定義新的getter和setter接口以對(duì)其進(jìn)行操作。這可以如下進(jìn)行。
class Celsius: def __init__(self, temperature = 0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # new update def get_temperature(self): return self._temperature def set_temperature(self, value): if value < -273: raise ValueError("-273度是不可能的") self._temperature = value
我們?cè)谏厦婵梢钥吹絞et_temperature(),set_temperature()已經(jīng)定義了新方法,此外,用_temperature替換了temperature。下劃線(_)開頭表示Python中的私有變量。
>>> c = Celsius(-277) Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible >>> c = Celsius(37) >>> c.get_temperature() 37 >>> c.set_temperature(10) >>> c.set_temperature(-300) Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible
此更新成功實(shí)施了新限制。我們不再被允許將溫度設(shè)置為低于-273。
請(qǐng)注意,私有變量在Python中不存在。只需遵循一些規(guī)范即可。語(yǔ)言本身沒有任何限制。
>>> c._temperature = -300 >>> c.get_temperature() -300
但這不是一個(gè)大問題。上述更新的最大問題在于,所有在程序中實(shí)現(xiàn)了上一類的客戶端都必須將其代碼從obj.temperature修改為obj.get_temperature(),并將所有分配(例如obj.temperature = val修改為obj.set_temperature( val))。
這種重構(gòu)會(huì)給客戶帶來數(shù)十萬(wàn)行代碼的麻煩。
總而言之,我們的新更新不向后兼容。這是@property發(fā)揮作用的地方。
python處理上述問題的方法是使用property。我們可以這樣來實(shí)現(xiàn)它。
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 def get_temperature(self): print("獲得的值") return self._temperature def set_temperature(self, value): if value < -273: raise ValueError("零下273度是不可能的") print("設(shè)定值") self._temperature = value temperature = property(get_temperature,set_temperature)
并且,一旦運(yùn)行,在shell中發(fā)出以下代碼。
>>> c = Celsius()
我們?cè)趃et temperature()和set temperature()中添加了print()函數(shù),以便清楚地觀察它們的執(zhí)行情況。
代碼的最后一行創(chuàng)建一個(gè)property對(duì)象temperature。簡(jiǎn)而言之,屬性將一些代碼(get_temperature和set_temperature)附加到成員屬性訪問(temperature)。
任何檢索溫度值的代碼都將自動(dòng)調(diào)用get_temperature()而不是字典(__dict__)查找。 同樣,任何為溫度分配值的代碼都會(huì)自動(dòng)調(diào)用set_temperature()。 這是Python中的一項(xiàng)很酷的功能。
我們可以在上面看到即使創(chuàng)建對(duì)象時(shí)也會(huì)調(diào)用set_temperature()。
你能猜出為什么嗎?
原因是創(chuàng)建對(duì)象時(shí),將調(diào)用__init __()方法。 此方法的線為self.temperature = temperature。 此分配自動(dòng)稱為set_temperature()。
>>> c.temperature Getting value 0
同樣,任何訪問如c.temperature都會(huì)自動(dòng)調(diào)用get_temperature()。 這就是屬性的作用。 這里還有一些實(shí)例。
>>> c.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001
通過使用屬性,我們可以看到,我們修改了類并實(shí)現(xiàn)了值約束,而無(wú)需更改客戶端代碼。因此,我們的實(shí)現(xiàn)是向后兼容的。
最后請(qǐng)注意,實(shí)際溫度值存儲(chǔ)在私有變量_temperature中。 temperature屬性是一個(gè)屬性對(duì)象,它提供了與此私有變量的接口。
在Python中,property()是一個(gè)內(nèi)置函數(shù),用于創(chuàng)建并返回屬性對(duì)象。該函數(shù)的簽名是
property(fget=None, fset=None, fdel=None, doc=None)
其中,fget為獲取屬性值的函數(shù),fset為設(shè)置屬性值的函數(shù),fdel為刪除屬性的函數(shù),doc為字符串(如注釋)。從實(shí)現(xiàn)中可以看出,這些函數(shù)參數(shù)是可選的。因此,可以簡(jiǎn)單地按照以下方式創(chuàng)建屬性對(duì)象。
>>> property() <property object at 0x0000000003239B38>
屬性對(duì)象有三個(gè)方法,getter()、setter()和deleter(),用于稍后指定fget、fset和fdel。這意味著
temperature = property(get_temperature,set_temperature)
也可以分解為
# 創(chuàng)建空屬性 temperature = property() # 設(shè)置 fget temperature = temperature.getter(get_temperature) # 設(shè)置 fset temperature = temperature.setter(set_temperature)
這兩段代碼是等效的。
熟悉Python中裝飾器的程序員可以認(rèn)識(shí)到上述構(gòu)造可以實(shí)現(xiàn)為裝飾器。
我們可以更進(jìn)一步,不定義名稱get_temperature,set_temperature,因?yàn)樗鼈兪遣槐匾?,并且?huì)影響類命名空間。為此,我們?cè)诙xgetter和setter函數(shù)時(shí)重用了名稱temperature。這是可以做到的。
class Celsius: def __init__(self, temperature = 0): self._temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 @property def temperature(self): print("獲得值") return self._temperature @temperature.setter def temperature(self, value): if value < -273: raise ValueError("零下273度是不可能的") print("設(shè)定值") self._temperature = value
上面的實(shí)現(xiàn)是制作屬性的簡(jiǎn)單方法和推薦方法。在Python中尋找屬性時(shí),您很可能會(huì)遇到這些類型的構(gòu)造。
好,今天就這樣。