使用@property

Python @property 装饰器教程

1. 为什么需要属性装饰器

在面向对象编程中,直接暴露类的属性虽然方便,但会失去对属性赋值的控制:

class Student:
    pass

s = Student()
s.score = 9999  # 可以随意设置不合法的值

为了解决这个问题,传统做法是使用 getter 和 setter 方法:

class Student:
    def get_score(self):
        return self._score
    
    def set_score(self, value):
        if not isinstance(value, int):
            raise ValueError('分数必须是整数!')
        if value < 0 or value > 100:
            raise ValueError('分数必须在0~100之间!')
        self._score = value

但这种方法调用不够简洁,Python 提供了更好的解决方案 - @property 装饰器。

2. @property 的基本用法

@property 装饰器可以将方法转换为属性,同时保持对赋值的控制:

class Student:
    @property
    def score(self):
        return self._score
    
    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('分数必须是整数!')
        if value < 0 or value > 100:
            raise ValueError('分数必须在0~100之间!')
        self._score = value

使用方式:

s = Student()
s.score = 60  # 调用setter方法
print(s.score)  # 调用getter方法
s.score = 9999  # 抛出ValueError

3. 只读属性

只定义 getter 方法而不定义 setter 方法可以创建只读属性:

class Student:
    @property
    def birth(self):
        return self._birth
    
    @birth.setter
    def birth(self, value):
        self._birth = value
    
    @property
    def age(self):
        return 2023 - self._birth  # 计算属性

使用示例:

s = Student()
s.birth = 2000  # 可以设置
print(s.age)    # 可以读取
s.age = 23      # 报错,因为是只读属性

4. 注意事项

  1. 避免属性名与方法名冲突
class Student:
    @property
    def birth(self):
        return self.birth  # 错误!会导致无限递归

正确做法是使用不同的名称,通常加下划线前缀:

class Student:
    @property
    def birth(self):
        return self._birth
  1. 属性初始化:在 __init__ 方法中初始化属性
class Student:
    def __init__(self):
        self._score = 0  # 初始化默认值

5. 实际应用示例

屏幕分辨率计算

class Screen:
    @property
    def width(self):
        return self._width
    
    @width.setter
    def width(self, value):
        if not isinstance(value, (int, float)) or value <= 0:
            raise ValueError('宽度必须是正数!')
        self._width = value
    
    @property
    def height(self):
        return self._height
    
    @height.setter
    def height(self, value):
        if not isinstance(value, (int, float)) or value <= 0:
            raise ValueError('高度必须是正数!')
        self._height = value
    
    @property
    def resolution(self):
        return self._width * self._height

测试代码:

s = Screen()
s.width = 1024
s.height = 768
print(f'分辨率 = {s.resolution}')  # 输出: 分辨率 = 786432

6. 最佳实践

  1. 使用 @property 封装需要验证或计算的属性
  2. 保持 getter 方法简单,避免复杂计算
  3. 在 setter 方法中进行输入验证
  4. 对于只读属性,只提供 getter 方法
  5. 使用下划线前缀命名内部存储变量

7. 总结

@property 装饰器是 Python 中实现属性访问控制的优雅方式,它:

  • 使代码更简洁直观
  • 保持对属性赋值的控制
  • 支持只读属性
  • 允许在访问属性时执行额外逻辑

通过合理使用 @property,可以编写出既安全又易用的类接口。