@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 编辑过