python中@property注解的具體使用
一、@property核心定義
@property 是 Python 內(nèi)置的裝飾器,用于將類的方法轉(zhuǎn)換為屬性式訪問。它允許開發(fā)者將方法以屬性的形式調(diào)用(如 obj.attr 而非 obj.attr()),同時(shí)封裝屬性的讀取(getter)、賦值(setter)、刪除(deleter)邏輯,實(shí)現(xiàn)受控的屬性訪問(如數(shù)據(jù)驗(yàn)證、計(jì)算屬性、隱藏內(nèi)部變量)。
核心價(jià)值:
- 替代傳統(tǒng)的
get_x()/set_x()方法,讓屬性訪問更直觀、符合 Python 風(fēng)格; - 兼顧封裝性(隱藏內(nèi)部變量)和易用性(屬性式調(diào)用);
- 支持動(dòng)態(tài)計(jì)算屬性(如根據(jù)其他屬性推導(dǎo)值)、數(shù)據(jù)校驗(yàn)(如限制屬性取值范圍)。
二、基礎(chǔ)用法
1. 只讀屬性(基礎(chǔ) getter)
最核心的用法是將方法轉(zhuǎn)為只讀屬性,適用于「計(jì)算屬性」(值由其他屬性推導(dǎo),無需手動(dòng)賦值)。
示例:圓的面積計(jì)算
class Circle:
def __init__(self, radius: float):
self.radius = radius # 基礎(chǔ)屬性
@property
def area(self) -> float:
"""計(jì)算面積(只讀屬性),無需調(diào)用方法,直接訪問"""
return 3.1415926 * self.radius **2
# 使用:像訪問普通屬性一樣調(diào)用,無需加 ()
c = Circle(5)
print(c.area) # 輸出:78.539815
# 嘗試賦值會(huì)報(bào)錯(cuò)(只讀屬性)
# c.area = 100 # AttributeError: can't set attribute
2. 可寫屬性(setter 裝飾器)
通過 @屬性名.setter 裝飾器定義賦值邏輯,實(shí)現(xiàn)屬性的可控寫入(如數(shù)據(jù)驗(yàn)證)。
示例:年齡屬性的校驗(yàn)
class Person:
def __init__(self, name: str, age: int):
self.name = name
self._age = age # 下劃線前綴:約定俗成的「私有變量」(Python 無真正私有)
@property
def age(self) -> int:
"""getter:讀取年齡"""
return self._age
@age.setter
def age(self, value: int):
"""setter:賦值時(shí)驗(yàn)證數(shù)據(jù)合法性"""
# 類型校驗(yàn)
if not isinstance(value, int):
raise TypeError("年齡必須是整數(shù)")
# 范圍校驗(yàn)
if value < 0 or value > 150:
raise ValueError("年齡必須在 0-150 之間")
self._age = value
# 使用:賦值時(shí)自動(dòng)觸發(fā) setter 校驗(yàn)
p = Person("Alice", 20)
print(p.age) # 20(觸發(fā) getter)
p.age = 25 # 合法賦值(觸發(fā) setter)
# p.age = "30" # 觸發(fā) TypeError
# p.age = 200 # 觸發(fā) ValueError
3. 可刪除屬性(deleter 裝飾器)
通過 @屬性名.deleter 裝飾器定義屬性刪除邏輯,適用于需要清理資源的場(chǎng)景。
示例:刪除年齡屬性
class Person:
# (繼承上面的 __init__、age getter/setter)
@age.deleter
def age(self):
"""deleter:刪除屬性時(shí)執(zhí)行的邏輯"""
print("刪除年齡屬性...")
del self._age
# 使用:刪除屬性觸發(fā) deleter
p = Person("Bob", 30)
del p.age # 輸出:刪除年齡屬性...
# 此時(shí)訪問 p.age 會(huì)報(bào)錯(cuò)(_age 已被刪除)
# print(p.age) # AttributeError: 'Person' object has no attribute '_age'
三、進(jìn)階用法
1. 與繼承結(jié)合:重寫父類的 property
子類可重寫父類的 @property 及其 setter/deleter,實(shí)現(xiàn)個(gè)性化邏輯。
class Student(Person):
@property
def age(self):
"""重寫 getter:添加學(xué)生年齡的額外提示"""
print(f"學(xué)生 {self.name} 的年齡:")
return super().age # 調(diào)用父類的 getter
@age.setter
def age(self, value):
"""重寫 setter:添加學(xué)生年齡的特殊校驗(yàn)"""
if value < 6 or value > 30:
raise ValueError("學(xué)生年齡應(yīng)在 6-30 之間")
super(Student, Student).age.fset(self, value) # 調(diào)用父類的 setter
# 測(cè)試
s = Student("Charlie", 18)
print(s.age) # 輸出:學(xué)生 Charlie 的年齡:18
s.age = 35 # 觸發(fā) ValueError
2. 緩存昂貴的計(jì)算屬性
對(duì)于計(jì)算耗時(shí)的屬性(如大數(shù)據(jù)處理、網(wǎng)絡(luò)請(qǐng)求),可結(jié)合 functools.lru_cache 緩存結(jié)果,避免重復(fù)計(jì)算。
import functools
class DataProcessor:
def __init__(self, raw_data: list[int]):
self.raw_data = raw_data
@property
@functools.lru_cache(maxsize=None) # 緩存方法結(jié)果
def processed_data(self) -> list[int]:
"""模擬昂貴的計(jì)算:僅首次調(diào)用時(shí)計(jì)算,后續(xù)直接返回緩存"""
print("執(zhí)行昂貴計(jì)算...")
return [x * 2 + 1 for x in self.raw_data]
# 測(cè)試
dp = DataProcessor([1, 2, 3, 4])
print(dp.processed_data) # 輸出:執(zhí)行昂貴計(jì)算... [3, 5, 7, 9]
print(dp.processed_data) # 直接返回緩存,無打印
3. 手動(dòng)創(chuàng)建 property 對(duì)象(非裝飾器方式)
@property 本質(zhì)是語(yǔ)法糖,底層通過 property() 類實(shí)現(xiàn)??墒謩?dòng)創(chuàng)建 property 對(duì)象,效果與裝飾器一致:
class Person:
def __init__(self, age: int):
self._age = age
# 定義 getter/setter/deleter 方法
def get_age(self):
return self._age
def set_age(self, value):
if value < 0:
raise ValueError("年齡不能為負(fù)")
self._age = value
def del_age(self):
del self._age
# 手動(dòng)創(chuàng)建 property 對(duì)象:property(fget, fset, fdel, doc)
age = property(get_age, set_age, del_age, "年齡屬性,支持校驗(yàn)")
# 使用方式與裝飾器一致
p = Person(20)
print(p.age) # 20
p.age = 25 # 合法
四、關(guān)鍵注意事項(xiàng)
1. 命名規(guī)范:避免變量名沖突
- 內(nèi)部存儲(chǔ)的變量建議用下劃線前綴(如
_age),與@property裝飾的方法名(如age)區(qū)分,避免無限遞歸:# 錯(cuò)誤示例:無限遞歸 class Person: def __init__(self, age): self.age = age # 賦值時(shí)觸發(fā) setter,setter 又賦值 self.age → 遞歸 @property def age(self): return self.age # 讀取時(shí)觸發(fā) getter,getter 又讀取 self.age → 遞歸 # 正確:內(nèi)部變量用 _age,property 方法用 age
2. 只讀屬性的限制
僅定義 @property(無 setter)的屬性是只讀的,嘗試賦值會(huì)拋出 AttributeError,適合純計(jì)算屬性。
五、@propertyvs 傳統(tǒng) getter/setter
| 特性 | @property | 傳統(tǒng) get_x()/set_x() |
|---|---|---|
| 調(diào)用方式 | obj.age(屬性式) | obj.get_age()(方法式) |
| 封裝性 | 隱藏內(nèi)部變量(如 _age) | 需手動(dòng)約定命名規(guī)范 |
| 數(shù)據(jù)校驗(yàn) | 賦值時(shí)自動(dòng)觸發(fā) setter 校驗(yàn) | 需手動(dòng)調(diào)用 set_x() 校驗(yàn) |
| 代碼可讀性 | 更簡(jiǎn)潔、符合 Python 風(fēng)格 | 冗余、不夠直觀 |
| 兼容性 | 僅新式類支持(Python 3 默認(rèn)) | 全版本兼容 |
六、適用場(chǎng)景總結(jié)
| 場(chǎng)景 | 推薦用法 |
|---|---|
| 計(jì)算屬性(如面積、總價(jià)) | 僅定義 @property(只讀) |
| 需要數(shù)據(jù)校驗(yàn)的屬性 | 定義 @property + @屬性名.setter |
| 需要清理資源的屬性 | 額外定義 @屬性名.deleter |
| 封裝內(nèi)部變量,對(duì)外提供統(tǒng)一訪問接口 | @property 替代手動(dòng) getter/setter |
| 復(fù)用屬性邏輯(多類共用) | 自定義描述符(而非 @property) |
七、總結(jié)
@property 是 Python 面向?qū)ο缶幊讨袑?shí)現(xiàn)屬性封裝的核心工具,它以簡(jiǎn)潔的語(yǔ)法糖封裝了描述符協(xié)議,兼顧了「易用性」(屬性式訪問)和「封裝性」(受控的讀寫邏輯)。無論是實(shí)現(xiàn)計(jì)算屬性、數(shù)據(jù)校驗(yàn),還是隱藏內(nèi)部變量,@property 都是比傳統(tǒng) getter/setter 更優(yōu)雅的選擇,是 Python 開發(fā)者必須掌握的特性之一。
到此這篇關(guān)于python中@property注解的具體使用的文章就介紹到這了,更多相關(guān)python @property注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- python @property的用法及含義全面解析
- 介紹Python的@property裝飾器的用法
- 實(shí)例講解Python編程中@property裝飾器的用法
- python 中的@property的用法詳解
- Python 中@property的用法詳解
- Python @property裝飾器原理解析
- 詳解Python裝飾器之@property
- Python進(jìn)階之@property動(dòng)態(tài)屬性的實(shí)現(xiàn)
- python裝飾器中@property屬性的使用解析
- Python @property原理解析和用法實(shí)例
- Python如何使用@property @x.setter及@x.deleter
- Python @property使用方法解析
- python中@Property屬性使用方法
相關(guān)文章
Python中用于轉(zhuǎn)換字母為小寫的lower()方法使用簡(jiǎn)介
這篇文章主要介紹了Python中用于轉(zhuǎn)換字母為小寫的lower()方法使用,是Python學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-05-05
Python如何按單元格讀取復(fù)雜電子表格(Excel)的數(shù)據(jù)
這篇文章主要介紹了Python如何按單元格讀取復(fù)雜電子表格(Excel)的數(shù)據(jù)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-06-06
使用Pandas計(jì)算系統(tǒng)客戶名稱的相似度
在日常業(yè)務(wù)處理中,我們經(jīng)常會(huì)面臨將不同系統(tǒng)中的數(shù)據(jù)進(jìn)行匹配和比對(duì)的情況,本文將介紹如何使用Python的Pandas庫(kù)來處理這個(gè)問題,需要的可以參考一下2023-07-07
Python生成隨機(jī)驗(yàn)證碼代碼實(shí)例解析
這篇文章主要介紹了Python生成隨機(jī)驗(yàn)證碼代碼實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
Python標(biāo)準(zhǔn)庫(kù)之typing的用法(類型標(biāo)注)
這篇文章主要介紹了Python標(biāo)準(zhǔn)庫(kù)之typing的用法(類型標(biāo)注),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
對(duì)Python新手編程過程中如何規(guī)避一些常見問題的建議
這篇文章中作者對(duì)Python新手編程過程中如何規(guī)避一些常見問題給出了建議,主要著眼于初學(xué)者對(duì)于一些常用函數(shù)方法在平時(shí)的使用習(xí)慣中的問題給出建議,需要的朋友可以參考下2015-04-04
python判斷一個(gè)數(shù)是否能被另一個(gè)整數(shù)整除的實(shí)例
今天小編就為大家分享一篇python判斷一個(gè)數(shù)是否能被另一個(gè)整數(shù)整除的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-12-12
Pycharm創(chuàng)建文件時(shí)自動(dòng)生成文件頭注釋(自定義設(shè)置作者日期)
這篇文章主要介紹了Pycharm創(chuàng)建文件時(shí)自動(dòng)生成文件頭注釋(自定義設(shè)置作者日期),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
python命令行運(yùn)行報(bào)錯(cuò)ModuleNotFoundError:No module named‘X
這篇文章主要介紹了python命令行運(yùn)行報(bào)錯(cuò)ModuleNotFoundError:No module named‘XXX‘找不到自定義模塊問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11

