實(shí)例講解Python編程中@property裝飾器的用法
取值和賦值
class Actress():
def __init__(self):
self.name = 'TianXin'
self.age = 5
類(lèi)Actress中有兩個(gè)成員變量name和age。在外部對(duì)類(lèi)的成員變量的操作,主要包括取值和賦值。簡(jiǎn)單的取值操作是x=object.var,簡(jiǎn)單的賦值操作是object.var=value。
>>> actress = Actress() >>> actress.name #取值操作 'TianXin' >>> actress.age #取值操作 20 >>> actress.name = 'NoName' #賦值操作 >>> actress.name 'NoName'
使用 Getter 和 Setter
上述簡(jiǎn)單的取值和賦值操作,在某些情況下是不能滿足要求的。比如,如果要限制Actress的年齡范圍,那么只使用上述簡(jiǎn)單的賦值操作就不能滿足要求了。getter和setter實(shí)現(xiàn)這樣的要求。
class Actress():
def __init__(self):
self._name = 'TianXin'
self._age = 20
def getAge(self):
return self._age
def setAge(self, age):
if age > 30:
raise ValueError
self._age = age
調(diào)用setAge函數(shù)可以實(shí)現(xiàn)將變量_age的取值范圍限制到小于30.
>>> actress = Actress() >>> actress.setAge(28) >>> actress.getAge() 28 >>> actress.setAge(35) ValueError
使用property
property的定義是:
其中,fget是取值函數(shù),fset是賦值函數(shù),fdel是刪除函數(shù)。使用property也實(shí)現(xiàn)上述對(duì)成員變量的取值限制。
class Actress():
def __init__(self):
self._name = 'TianXin'
self._age = 20
def getAge(self):
return self._age
def setAge(self, age):
if age > 30:
raise ValueError
self._age = age
age=property(getAge, setAge, None, 'age property')
經(jīng)過(guò)上面的定義后,可以像簡(jiǎn)單取值和賦值操作一樣操作age。比如,
>>> actress = Actress() >>> actress.age 20 >>> actress.age = 18 >>> actress.age = 55 ValueError
使用@property
使用@property同樣可以實(shí)現(xiàn)上述類(lèi)的定義。
class Actress():
def __init__(self):
self._name = 'TianXin'
self._age = 20
@property
def age(self):
return self._age
@age.setter
def age(self, age):
if age > 30:
raise ValueError
self._age = age
使用時(shí)的示例:
>>> actress = Actress() >>> actress.age 20 >>> actress.age = 18 >>> actress.age = 45 ValueError
Python2 和 Python3中使用property的區(qū)別
上述property示例在Python3的環(huán)境下有效。在Python2中,使用property時(shí),類(lèi)定義時(shí)需要繼承object。否則,property的賦值操作不可使用。
Python2下property的正確使用方式:
class Actress(object): #差別在這里
def __init__(self):
self._name = 'TianXin'
self._age = 20
@property
def age(self):
return self._age
@age.setter
def age(self, age):
if age > 30:
raise ValueError
self._age = age
def setName(self, name):
self._name = name
def getName(self):
return self._name
def delName(self):
print('Goodbye...')
del self._name
name = property(getName, setName, delName, 'name property'
)
從前,Python程序員Alice要打算創(chuàng)建一個(gè)代表金錢(qián)的類(lèi)。她的第一個(gè)實(shí)現(xiàn)形式大概是下面這樣:
# 以美元為基礎(chǔ)貨幣的Money類(lèi)的首個(gè)版本
class Money:
def __init__(self, dollars, cents):
self.dollars = dollars
self.cents = cents
# 還有其他一些方法,我們暫時(shí)不必理會(huì)
這個(gè)類(lèi)后來(lái)被打包到一個(gè)Python庫(kù)里,并且慢慢地被許多不同的應(yīng)用使用。舉個(gè)例子,另一個(gè)團(tuán)隊(duì)中的Python程序員Bob是這樣使用Money類(lèi)的:
money = Money(27, 12)
message = "I have {:d} dollars and {:d} cents."
print(message.format(money.dollars, money.cents))
# "I have 27 dollars and 12 cents."
money.dollars += 2
money.cents += 20
print(message.format(money.dollars, money.cents))
# "I have 29 dollars and 32 cents."
這樣使用并沒(méi)有錯(cuò),但是卻出現(xiàn)了代碼可維護(hù)性的問(wèn)題。你發(fā)現(xiàn)了嗎?
幾個(gè)月或是幾年之后。Alice想要重構(gòu)Money類(lèi)的內(nèi)部實(shí)現(xiàn),不再記錄美元和美分,而是僅僅記錄美分,因?yàn)檫@樣做可以讓某些操作簡(jiǎn)單很多。下面是她很可能會(huì)作的修改:
# Money類(lèi)的第二個(gè)版本
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
這一修改帶來(lái)一個(gè)后果:引用Money類(lèi)的每一行代碼都必須要調(diào)整。有時(shí)候很幸運(yùn),你就是所有這些代碼的維護(hù)者,只需要自己直接重構(gòu)即可。但是Alice的情況就沒(méi)有這么好了;許多團(tuán)隊(duì)都復(fù)用了她的代碼。因此,她需要協(xié)調(diào)他們的代碼庫(kù)與自己的修改保持一致,也許甚至要經(jīng)歷一段特別痛苦、漫長(zhǎng)的正式棄用過(guò)程(deprecation process)。
幸運(yùn)的是,Alice知道一種更好的解決辦法,可以避免這個(gè)令人頭疼的局面出現(xiàn):使用Python內(nèi)建的property裝飾器。@property一般應(yīng)用在Python方法上,可以有效地將屬性訪問(wèn)(attribute access)變成方法調(diào)用(method call)。舉個(gè)例子,暫時(shí)將Money類(lèi)拋至一邊,假設(shè)有一個(gè)代表人類(lèi)的Person類(lèi)(class):
class Person:
def __init__(self, first, last):
self.first = first
self.last = last
@property
def full_name(self):
return '{} {}'.format(self.first, self.last)
代碼樣式不同,是因?yàn)橹坝玫墓ぞ叱鰡?wèn)題了?!狤arlGrey
請(qǐng)注意full_name方法。除了在def語(yǔ)句上方裝飾了@property之外,該方法的聲明沒(méi)有什么不同的地方。但是,這卻改變了Person對(duì)象的運(yùn)作方式:
>>> buddy = Person('Jonathan', 'Doe')
>>> buddy.full_name
'Jonathan Doe'
我們發(fā)現(xiàn),盡管full_name被定義為一個(gè)方法,但卻可以通過(guò)變量屬性的方式訪問(wèn)。在最后一行代碼中沒(méi)有()操作符;我并沒(méi)有調(diào)用full_name方法。我們所做的,可以說(shuō)是創(chuàng)建了某種動(dòng)態(tài)屬性。
回到本文中的Money類(lèi),Alice對(duì)它作了如下修改:
# Money類(lèi)的最終版本
class Money:
def __init__(self, dollars, cents):
self.total_cents = dollars * 100 + cents
# Getter and setter for dollars...
@property
def dollars(self):
return self.total_cents // 100;
@dollars.setter
def dollars(self, new_dollars):
self.total_cents = 100 * new_dollars + self.cents
# And the getter and setter for cents.
@property
def cents(self):
return self.total_cents % 100;
@cents.setter
def cents(self, new_cents):
self.total_cents = 100 * self.dollars + new_cents
除了使用@property裝飾器定義了dollars屬性的getter外,Alice還利用@dollars.setter創(chuàng)建了一個(gè)setter。Alice還對(duì)cents`屬性作了類(lèi)似處理。
那么現(xiàn)在,Bob的代碼要做哪些相應(yīng)的修改呢?根本不用改!
# 他的代碼完全沒(méi)有變動(dòng),但是卻可以正常調(diào)用Money類(lèi)。
money = Money(27, 12)
message = "I have {:d} dollars and {:d} cents."
print(message.format(money.dollars, money.cents))
# "I have 27 dollars and 12 cents."
money.dollars += 2
money.cents += 20
print(message.format(money.dollars, money.cents))
# "I have 29 dollars and 32 cents."# 代碼邏輯也沒(méi)有問(wèn)題。
money.cents += 112
print(message.format(money.dollars, money.cents))
# "I have 30 dollars and 44 cents."
事實(shí)上,所有使用了Money類(lèi)的代碼都不需要進(jìn)行修改。Bob不知道或根本不在乎Alice去除了類(lèi)中的dollars和cents屬性:他的代碼還是和以前一樣正常執(zhí)行。唯一修改過(guò)的代碼就是Money類(lèi)本身。
正是由于Python中處理裝飾器的方式,你可以在類(lèi)中自由使用簡(jiǎn)單的屬性。如果你所寫(xiě)的類(lèi)改變了管理狀態(tài)的方法,你可以自信地通過(guò)@property裝飾器對(duì)這個(gè)類(lèi)(且只有這個(gè)類(lèi))進(jìn)行修改。這是一個(gè)共贏的方法!相反,在Java等語(yǔ)言中,程序員必須主動(dòng)去定義訪問(wèn)屬性的方法(例如getDollars或setCents)。
最后要提示大家:這種方法對(duì)于那些被其他程序員和團(tuán)隊(duì)復(fù)用的代碼最為重要。假設(shè)僅僅是在你自己一個(gè)維護(hù)的應(yīng)用中創(chuàng)建一個(gè)類(lèi)似Money的類(lèi),那么如果你改變了Money的接口,你只需要重構(gòu)自己的代碼就可以。這種情況下,你沒(méi)有必要像上面說(shuō)的那樣使用@property裝飾器。
相關(guān)文章
基于python 開(kāi)立方的注意事項(xiàng)說(shuō)明
這篇文章主要介紹了基于python 開(kāi)立方的注意事項(xiàng)說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03
Python?字典中鍵映射多個(gè)值的問(wèn)題解決
本文主要介紹了在Python中實(shí)現(xiàn)一個(gè)字典multidict中鍵可以對(duì)應(yīng)多個(gè)值,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-01-01
Python爬取破解無(wú)線網(wǎng)絡(luò)wifi密碼過(guò)程解析
這篇文章主要介紹了Python爬取破解無(wú)線網(wǎng)絡(luò)密碼過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
pandas實(shí)戰(zhàn):分析三國(guó)志人物示例實(shí)現(xiàn)
這篇文章主要介紹了pandas實(shí)戰(zhàn):分析三國(guó)志人物示例實(shí)現(xiàn),本文章內(nèi)容詳細(xì),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,需要的朋友可以參考下2023-01-01
python 密碼學(xué)示例——?jiǎng)P撒密碼的實(shí)現(xiàn)
這篇文章主要介紹了python 密碼學(xué)示例——?jiǎng)P撒密碼的實(shí)現(xiàn),幫助大家更好的利用python處理密碼,感興趣的朋友可以了解下2020-09-09
Scrapy爬蟲(chóng)框架集成selenium及全面詳細(xì)講解
這篇文章主要為大家介紹了Scrapy集成selenium,以及scarpy爬蟲(chóng)框架全面講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04
Python 簡(jiǎn)單計(jì)算要求形狀面積的實(shí)例
今天小編就為大家分享一篇Python 簡(jiǎn)單計(jì)算要求形狀面積的實(shí)例,具有很好的參考價(jià)值2020-01-01
利用Python構(gòu)建Flutter應(yīng)用的教程詳解
Flutter在軟件研發(fā)領(lǐng)域是非常流行的,今天就讓我們深入了解一下,用?Python構(gòu)建flutter應(yīng)用程序的世界,感興趣的小伙伴可以跟隨小編一起了解一下2022-12-12

