Python @property裝飾器全解析
在 Python 中,@property 是一個非常常用、也很優(yōu)雅的裝飾器,用來把“方法”偽裝成“屬性”。它最常見的用途是:對屬性的訪問進(jìn)行控制,同時保持簡潔的屬性訪問語法。
下面從幾個方面幫你系統(tǒng)介紹一下 @property:
一、為什么需要@property?
假設(shè)你有一個類,直接暴露公共屬性:
class Person:
def __init__(self, name, age):
self.name = name # 公共屬性
self.age = age # 公共屬性一開始你只是簡單存?zhèn)€年齡,后來你發(fā)現(xiàn):
- 想對
age做合法性檢查(不能是負(fù)數(shù)) - 想在設(shè)置年齡時做一些額外處理(比如同步更新別的數(shù)據(jù))
如果屬性是直接暴露的:
p = Person("Tom", 20)
p.age = -5 # 沒有任何阻止
你可以改成用方法:
class Person:
def __init__(self, name, age):
self._age = None
self.set_age(age)
def get_age(self):
return self._age
def set_age(self, value):
if value < 0:
raise ValueError("年齡不能為負(fù)數(shù)")
self._age = value但這樣使用時會變成:
p = Person("Tom", 20)
p.set_age(30)
print(p.get_age())
寫起來不如屬性直觀。并且如果項(xiàng)目已經(jīng)有很多地方寫了 p.age,現(xiàn)在改成 p.get_age() / p.set_age() 會造成兼容性問題。
@property 的作用就是:
在“不改變外部訪問方式”的前提下,為屬性增加 getter/setter 等邏輯。
二、@property的基本用法(只讀屬性)
最簡單的用法是將一個方法變成“只讀屬性”:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self): # getter
return self._radius使用時:
c = Circle(10) print(c.radius) # 像訪問屬性一樣調(diào)用,實(shí)際調(diào)用的是 radius() 方法 # 輸出:10 c.radius = 20 # 這里會報(bào)錯,因?yàn)闆]有定義 setter # AttributeError: can't set attribute
此時 radius 就是一個只讀屬性:可以獲取,不能直接賦值。
三、定義可讀可寫屬性:@xxx.setter
常見場景是既要讀,也要寫,并在寫入時做檢查或處理:
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
"""半徑屬性:讀取時從 _radius 取值"""
return self._radius
@radius.setter
def radius(self, value):
"""設(shè)置半徑時進(jìn)行校驗(yàn)"""
if value <= 0:
raise ValueError("半徑必須為正數(shù)")
self._radius = value使用:
c = Circle(10) print(c.radius) # 10 c.radius = 5 # 實(shí)際調(diào)用 radius.setter print(c.radius) # 5 c.radius = -1 # ValueError: 半徑必須為正數(shù)
要點(diǎn):
@radius.setter中的名字必須和@property的函數(shù)名完全一致。- setter 只定義“寫邏輯”,讀邏輯仍然在
@property的函數(shù)里。
四、帶有刪除邏輯的屬性:@xxx.deleter
有時你希望通過 del obj.attr 來觸發(fā)一些自定義行為,可以用 @xxx.deleter:
class Demo:
def __init__(self, value):
self._value = value
@property
def value(self):
print("獲取 value")
return self._value
@value.setter
def value(self, v):
print("設(shè)置 value")
self._value = v
@value.deleter
def value(self):
print("刪除 value")
del self._value使用:
d = Demo(10) print(d.value) # 獲取 value -> 10 d.value = 20 # 設(shè)置 value del d.value # 刪除 value
五、計(jì)算屬性:不存儲結(jié)果,只在訪問時計(jì)算
@property 不一定要綁定一個內(nèi)部存儲字段,也可以每次訪問時動態(tài)計(jì)算:
import math
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, r):
if r <= 0:
raise ValueError("半徑必須為正數(shù)")
self._radius = r
@property
def area(self):
"""面積屬性:只讀,實(shí)時計(jì)算"""
return math.pi * self._radius ** 2使用:
c = Circle(10) print(c.area) # 314.159... c.radius = 5 print(c.area) # 會自動變成半徑為 5 時的面積
area 不需要額外存儲字段 _area,也不需要在每次修改半徑時手動更新面積,訪問時自動按最新半徑計(jì)算。
六、命名習(xí)慣:前有下劃線的“內(nèi)部屬性”
常見寫法:
- 公共接口屬性:
obj.x - 內(nèi)部真實(shí)存儲字段:
obj._x
class Person:
def __init__(self, age):
self._age = None
self.age = age # 這里會走 setter
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("年齡不能為負(fù)數(shù)")
self._age = value初學(xué)者常見錯誤是 setter 里面寫成 self.age = value:
@age.setter
def age(self, value):
self.age = value # 錯誤!會導(dǎo)致無限遞歸
正確做法是操作“內(nèi)部字段”例如 self._age,避免調(diào)用自己。
七、@property的優(yōu)點(diǎn)總結(jié)
保持接口穩(wěn)定
一開始可以簡單寫成公有屬性:
class Person:
def __init__(self, age):
self.age = age
以后如果需要加校驗(yàn),只要改成:
class Person:
def __init__(self, age):
self._age = None
self.age = age
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("年齡不能為負(fù)數(shù)")
self._age = value外部代碼依然寫 p.age,不需要改任何調(diào)用。
語法簡潔、可讀性好
相比 obj.get_x() / obj.set_x(),obj.x 更符合屬性的直覺。
延遲計(jì)算 / 動態(tài)計(jì)算
對一些計(jì)算開銷不大的數(shù)據(jù),用 @property 動態(tài)計(jì)算非常方便。
如果計(jì)算很昂貴,還可以結(jié)合緩存(例如自己實(shí)現(xiàn)緩存,或用 functools.cached_property)。
方便加入調(diào)試日志、權(quán)限控制等邏輯
例如在 setter 里打印日志、做權(quán)限檢查,或者在 getter 里做懶加載等。
八、與傳統(tǒng) getter/setter 的區(qū)別
- Java / C++ 風(fēng)格:顯式的
get_xxx()、set_xxx()方法。 - Python:更提倡屬性訪問風(fēng)格 +
@property隱藏實(shí)現(xiàn)細(xì)節(jié)。
也就是說,Python 代碼里你幾乎看不到:
obj.get_age() obj.set_age(20)
而是:
obj.age obj.age = 20
但又不犧牲封裝性:內(nèi)部實(shí)現(xiàn)完全可以通過 @property 控制。
九、一些注意事項(xiàng)
- 不要濫用
@property- 若屬性只是單純存取,不需要任何檢查或計(jì)算,直接公有字段也完全沒問題。
- 過度包裝會增加心智負(fù)擔(dān)。
- 避免在 getter 中做特別耗時的操作
- 因?yàn)樵L問語法很輕量,調(diào)用者不會意識到“這個屬性代價(jià)這么大”,容易造成性能問題。
- 對耗時計(jì)算,建議用普通方法,或者明確注釋說明。
- 繼承時的覆蓋
- 子類可以重寫父類的
@property,仍然使用同名屬性,這對庫設(shè)計(jì)非常有用。
- 子類可以重寫父類的
十、一個綜合示例
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self._balance = None
self.balance = balance # 走 setter 做校驗(yàn)
@property
def balance(self):
"""賬戶余額,不能為負(fù)數(shù)"""
return self._balance
@balance.setter
def balance(self, value):
if value < 0:
raise ValueError("余額不能為負(fù)數(shù)")
self._balance = float(value)
@property
def is_rich(self):
"""計(jì)算屬性:根據(jù)余額判斷是否“富有”"""
return self._balance >= 1_000_000使用:
acc = BankAccount("Alice", 1000)
print(acc.balance) # 1000.0
acc.balance = 2000
print(acc.is_rich) # False
acc.balance = 2_000_000
print(acc.is_rich) # True
acc.balance = -10 # ValueError: 余額不能為負(fù)數(shù)到此這篇關(guān)于Python @property裝飾器全解析的文章就介紹到這了,更多相關(guān)Python @property裝飾器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python中pyserial 實(shí)現(xiàn)模擬串口通信的示例詳解
Python通過?pyserial?庫提供了強(qiáng)大的串口通信支持,使得開發(fā)者能夠快速實(shí)現(xiàn)串口數(shù)據(jù)的發(fā)送與接收,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-06-06
1分鐘快速生成用于網(wǎng)頁內(nèi)容提取的xslt
這篇文章主要教大家如何1分鐘快速生成用于網(wǎng)頁內(nèi)容提取的xslt,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Python如何聲明以管理員方式運(yùn)行(附實(shí)戰(zhàn)案例)
由于Windows的安全機(jī)制,Python寫的腳本缺少了管理員權(quán)限,運(yùn)行就會受到一些限制,這篇文章主要介紹了Python如何聲明以管理員方式運(yùn)行的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-04-04
Python實(shí)現(xiàn)Excel和TXT文本之間相互轉(zhuǎn)換
Excel是一種具有強(qiáng)大的數(shù)據(jù)處理和圖表制作功能的電子表格文件,而TXT則是一種簡單通用、易于編輯的純文本文件,本文將介紹如何使用Python并結(jié)合相關(guān)庫來實(shí)現(xiàn) Excel 和 TXT 文本文件之間的相互轉(zhuǎn)換,需要的朋友可以參考下2024-06-06
Python實(shí)現(xiàn)的爬取百度貼吧圖片功能完整示例
這篇文章主要介紹了Python實(shí)現(xiàn)的爬取百度貼吧圖片功能,結(jié)合完整實(shí)例形式分析了Python實(shí)現(xiàn)的百度貼吧圖片爬蟲相關(guān)操作技巧,需要的朋友可以參考下2019-05-05

