@property
裝飾器是 Python 中實現屬性訪問控制的強大工具,它允許你將方法轉換為"動態屬性",提供更精細的屬性訪問控制。
在面向對象編程中,直接暴露類的屬性存在以下問題:
- 無法驗證數據:無法在賦值時檢查數據有效性
- 無法動態計算:屬性值不能基于其他屬性動態計算
- 破壞封裝性:外部可直接修改內部狀態
@property
解決了這些問題,讓你能夠:
class MyClass:
def__init__(self, value):
self._value = value # 內部使用帶下劃線的變量名
@property
defvalue(self):
"""Getter方法:訪問屬性時調用"""
returnself._value
@value.setter
defvalue(self, new_value):
"""Setter方法:設置屬性時調用"""
if new_value < 0:
raise ValueError("值不能為負數")
self._value = new_value
@value.deleter
defvalue(self):
"""Deleter方法:刪除屬性時調用"""
print("刪除值!")
delself._value
1. 只讀屬性(無 setter)
class Circle:
def__init__(self, radius):
self.radius = radius
@property
defarea(self):
"""只讀屬性:計算圓的面積"""
return3.14 * self.radius ** 2
# 使用
c = Circle(5)
print(c.area) # 78.5
c.area = 100# 報錯:AttributeError: can't set attribute
2. 數據驗證
class Temperature:
def__init__(self, celsius):
self.celsius = celsius # 使用setter
@property
defcelsius(self):
returnself._celsius
@celsius.setter
defcelsius(self, value):
if value < -273.15:
raise ValueError("溫度不能低于絕對零度")
self._celsius = value
# 使用
temp = Temperature(25)
temp.celsius = -300# ValueError: 溫度不能低于絕對零度
3. 屬性別名
class Person:
def__init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
@property
deffull_name(self):
"""動態計算全名"""
returnf"{self.first_name} {self.last_name}"
@full_name.setter
deffull_name(self, name):
"""通過全名設置姓名"""
first, last = name.split(" ", 1)
self.first_name = first
self.last_name = last
# 使用
p = Person("張", "三")
print(p.full_name) # 張 三
p.full_name = "李 四"
print(p.first_name) # 李
1. 屬性緩存
class ExpensiveCalculation:
def__init__(self):
self._result = None
@property
defresult(self):
"""緩存計算結果"""
ifself._result isNone:
print("執行復雜計算...")
self._result = self._calculate()
returnself._result
def_calculate(self):
# 模擬復雜計算
returnsum(range(1, 1000000))
# 使用
calc = ExpensiveCalculation()
print(calc.result) # 第一次調用會計算
print(calc.result) # 直接返回緩存結果
2. 屬性依賴
class Rectangle:
def__init__(self, width, height):
self.width = width
self.height = height
@property
defarea(self):
returnself.width * self.height
@property
defperimeter(self):
return2 * (self.width + self.height)
@property
defaspect_ratio(self):
"""寬高比(只讀)"""
returnself.width / self.height
@aspect_ratio.setter
defaspect_ratio(self, ratio):
"""通過寬高比設置尺寸,保持面積不變"""
current_area = self.area
self.width = (current_area * ratio) ** 0.5
self.height = current_area / self.width
# 使用
rect = Rectangle(4, 3)
print(f"原始尺寸: {rect.width}x{rect.height}") # 4x3
rect.aspect_ratio = 16/9
print(f"新尺寸: {rect.width:.1f}x{rect.height:.1f}") # 6.5x3.7
命名規范:
性能考慮:
何時使用:
常見錯誤:
class BadExample:
@property
def value(self):
return self.value # 遞歸調用自身!
@value.setter
def value(self, new_value):
self.value = new_value # 遞歸設置!
正確做法:使用帶下劃線的內部變量
與普通屬性的區別:
選擇建議:
配置管理系統
class AppConfig:
def__init__(self):
self._settings = {}
self._cache = {}
@property
deftheme(self):
"""獲取當前主題"""
returnself._settings.get('theme', 'light')
@theme.setter
deftheme(self, value):
"""設置主題并驗證"""
if value notin ['light', 'dark', 'system']:
raise ValueError("無效的主題選項")
self._settings['theme'] = value
self._cache.clear() # 清除緩存
@property
defapi_endpoint(self):
"""動態構建API端點"""
if'api_endpoint'notinself._cache:
base_url = self._settings.get('base_url', 'https://api.example.com')
version = self._settings.get('api_version', 'v1')
self._cache['api_endpoint'] = f"{base_url}/{version}/"
returnself._cache['api_endpoint']
# 使用
config = AppConfig()
config.theme = 'dark'
print(config.theme) # dark
print(config.api_endpoint) # https://api.example.com/v1/
@property
裝飾器是 Python 面向對象編程中不可或缺的工具:
關鍵要點:
- 使用
@property
定義 getter 方法 - 使用
@x.setter
定義 setter 方法 - 使用
@x.deleter
定義 deleter 方法
掌握 @property
更加 Pythonic地設計類,在保持接口簡潔的同時實現強大的屬性控制功能。
閱讀原文:原文鏈接
該文章在 2025/7/18 10:34:59 編輯過