property 是 Python 中用于管理类属性访问的核心工具,它能将类的方法“伪装”成属性,在保持代码简洁性的同时,实现属性的封装、验证、计算等高级功能。

一、property 的核心作用

1. 封装内部实现

隐藏属性的具体操作逻辑(如数据存储方式、计算过程),仅对外暴露简洁的属性访问接口。

2. 数据验证与保护

在设置属性值时自动执行验证逻辑(如范围检查、类型校验),防止非法数据破坏对象状态。

3. 动态计算属性值

根据其他属性动态计算结果(无需额外存储),确保数据一致性。

4. 向后兼容

当类的内部实现变更时(如将直接属性访问改为方法调用),无需修改外部代码。

二、property 的两种用法

用法 1:@property 装饰器(推荐,更简洁)

通过装饰器将方法转换为属性,分为 getter(读取)、setter(设置)、deleter(删除)三个部分。

语法结构
class MyClass:
    @property
    def my_attr(self):
        # getter:读取属性时调用
        return self._my_attr  # 通常用下划线开头表示内部属性

    @my_attr.setter
    def my_attr(self, value):
        # setter:设置属性时调用(可选)
        # 可在此添加验证逻辑
        self._my_attr = value

    @my_attr.deleter
    def my_attr(self):
        # deleter:删除属性时调用(可选)
        del self._my_attr
完整案例:学生成绩管理系统
class Student:
    def __init__(self, name, score):
        self.name = name  # 普通属性
        self.score = score  # 受 property 管理的属性

    # ------------------------------
    # 1. score 属性的 getter(读取)
    # ------------------------------
    @property
    def score(self):
        """获取学生成绩(自动返回内部存储的 _score)"""
        return self._score

    # ------------------------------
    # 2. score 属性的 setter(设置,含验证)
    # ------------------------------
    @score.setter
    def score(self, value):
        """设置学生成绩(必须在 0-100 之间)"""
        # 数据验证:类型检查
        if not isinstance(value, (int, float)):
            raise TypeError("成绩必须是数字(int 或 float)")
        # 数据验证:范围检查
        if not (0 <= value <= 100):
            raise ValueError("成绩必须在 0-100 之间")
        # 验证通过后,存储到内部属性 _score
        self._score = value

    # ------------------------------
    # 3. 动态计算属性:grade(根据 score 自动计算等级)
    # ------------------------------
    @property
    def grade(self):
        """根据成绩动态返回等级(A/B/C/D/F)"""
        if self._score >= 90:
            return "A"
        elif self._score >= 80:
            return "B"
        elif self._score >= 70:
            return "C"
        elif self._score >= 60:
            return "D"
        else:
            return "F"

    # ------------------------------
    # 4. deleter:删除 score 时的清理逻辑
    # ------------------------------
    @score.deleter
    def score(self):
        """删除成绩时的提示(可选)"""
        print(f"已删除学生 {self.name} 的成绩")
        del self._score


# ------------------------------
# 测试案例
# ------------------------------
if __name__ == "__main__":
    # 1. 创建学生对象(合法成绩)
    try:
        student = Student("张三", 85)
        print(f"姓名:{student.name}")
        print(f"成绩:{student.score}")  # 像访问属性一样调用 getter
        print(f"等级:{student.grade}")  # 动态计算的属性
    except (TypeError, ValueError) as e:
        print(f"初始化失败:{e}")

    # 输出:
    # 姓名:张三
    # 成绩:85
    # 等级:B

    # 2. 尝试设置非法成绩(验证逻辑生效)
    try:
        student.score = 105  # 超出范围
    except ValueError as e:
        print(f"设置失败:{e}")  # 输出:设置失败:成绩必须在 0-100 之间

    try:
        student.score = "优秀"  # 类型错误
    except TypeError as e:
        print(f"设置失败:{e}")  # 输出:设置失败:成绩必须是数字(int 或 float)

    # 3. 修改成绩后,等级自动更新
    student.score = 92
    print(f"\n修改后成绩:{student.score}")
    print(f"修改后等级:{student.grade}")  # 输出:A

    # 4. 删除成绩(触发 deleter)
    del student.score  # 输出:已删除学生 张三 的成绩

用法 2:property() 函数(传统方式)

通过内置函数 property() 直接创建属性对象,语法为:

property(fget=None, fset=None, fdel=None, doc=None)
  • fget:获取属性的方法(getter)
  • fset:设置属性的方法(setter)
  • fdel:删除属性的方法(deleter)
  • doc:属性的文档字符串
案例:用 property() 重写学生类
class Student:
    def __init__(self, name, score):
        self.name = name
        self._score = None  # 内部存储的属性
        self.score = score  # 通过 setter 初始化

    # getter 方法
    def _get_score(self):
        return self._score

    # setter 方法(含验证)
    def _set_score(self, value):
        if not isinstance(value, (int, float)):
            raise TypeError("成绩必须是数字")
        if not (0 <= value <= 100):
            raise ValueError("成绩必须在 0-100 之间")
        self._score = value

    # deleter 方法
    def _del_score(self):
        print(f"已删除 {self.name} 的成绩")
        del self._score

    # 动态计算 grade 的方法
    def _get_grade(self):
        if self._score >= 90:
            return "A"
        elif self._score >= 80:
            return "B"
        # ... 省略其他等级逻辑
        else:
            return "F"

    # ------------------------------
    # 使用 property() 创建属性
    # ------------------------------
    score = property(
        fget=_get_score,
        fset=_set_score,
        fdel=_del_score,
        doc="学生成绩(0-100)"
    )

    grade = property(fget=_get_grade)  # 只读属性(无 setter)


# 测试(与装饰器方式效果一致)
student = Student("李四", 78)
print(student.score)  # 78
print(student.grade)  # C

三、关键注意事项

1. 内部属性命名规范

通常用单下划线(如 _score)表示“内部使用的属性”,提醒外部代码不要直接访问(但 Python 不会强制禁止)。

2. 只读属性的实现

如果只定义 getter 而不定义 setter,属性将变为只读,尝试修改会抛出 AttributeError

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius  # 只有 getter,无 setter

c = Circle(5)
print(c.radius)  # 5
# c.radius = 10  # 报错:AttributeError: can't set attribute

3. 避免无限递归

如果在 getter/setter 中直接访问属性本身(而非内部属性),会导致无限递归:

# 错误示例!
class BadExample:
    @property
    def value(self):
        return self.value  # 无限递归调用 getter!

    @value.setter
    def value(self, v):
        self.value = v  # 无限递归调用 setter!

解决方法:始终用内部属性(如 _value)存储数据。

四、总结

  • 推荐使用 @property 装饰器:代码更简洁、可读性更强,符合 Python 风格。
  • 核心场景:数据验证、动态计算属性、封装内部逻辑。
  • 命名规范:内部属性用单下划线前缀,避免与 property 属性冲突。

通过 property,你可以在保持接口简洁的同时,为属性添加强大的管理逻辑,是 Python 面向对象编程中不可或缺的工具!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐