Python @property装饰器基础语法与简单示例

听雨眠
听雨眠
发布时间:2026-10-25| 5人浏览过
原创

在 Python 面向对象编程中,@property 是一个内置的高频装饰器。它的核心作用是将一个类方法伪装成普通属性,使得外部调用者在读取数据时无需加圆括号 (),同时在类内部保留方法计算与逻辑控制的能力。利用 @property,开发者既能保持字段访问的简洁性,又能在需要时无缝扩展数值校验、动态计算或访问拦截,而无需破坏现有的外部调用接口。

一、为什么需要 @property:方法属性化的优势

在很多传统面向对象语言(如 Java)中,通常推荐将属性设为私有,并通过专门的 get_attr() 和 set_attr() 方法来进行安全读写。这种做法虽然保证了封装性,但在 Python 中频繁书写 get_xxx() 会显得代码冗长且不符合简洁直观的代码风格。

反之,如果直接将变量公开给外部随意赋值,虽然书写简单,但一旦后续需要对输入数值进行有效性检查(例如价格不能为负、年龄不能超过合法范围),或者将某个静态属性重构成依赖其他字段的动态计算属性,就必须将原有的属性访问修改为方法调用,这会导致所有依赖该类的旧代码全部报错。

@property 完美化解了这一矛盾。它允许我们在外部像访问常规属性一样使用 obj.attr,在底层则自动触发对应的方法执行。

Python @property装饰器基础语法与简单示例
图 1:传统 Getter/Setter 方式与 @property 属性化机制的对比

二、基础语法:将方法转为只读属性 (Getter)

最基本的使用方式是直接在类的方法上方添加 @property。修饰后,该方法就成为了该属性的 Getter(取值器)。

1. 动态计算面积的代码示例

以矩形类为例,我们只需要保存长和宽两个基本属性,面积值随着长宽的变化实时计算得出,不需要额外开辟变量同步存储:

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    @property
    def area(self):
        """计算矩形面积,作为只读属性暴露"""
        return self.width * self.height

# 实例化对象
rect = Rectangle(10, 5)

# 像普通属性一样读取,不要加圆括号
print(rect.area)  # 输出示例: 50

# 动态修改基础属性后,面积自动同步
rect.width = 20
print(rect.area)  # 输出示例: 100

2. 只读特性验证

当一个方法仅用 @property 修饰,而没有定义对应的 setter 时,该属性是只读的。如果外部尝试对其直接赋值,Python 会主动抛出 AttributeError:

# 尝试对只读属性直接赋值
try:
    rect.area = 200
except AttributeError as err:
    print(f"抛出异常: {err}")
    # 输出示例: 抛出异常: property 'area' of 'Rectangle' object has no setter
注意:使用 @property 标记的方法,其参数列表除了代表实例自身的 self 之外,不能包含其他多余的位置参数。

三、实现可读写属性:配套使用 @setter 装饰器

如果希望某个被 @property 保护的属性能够被重新赋值,并且在赋值时执行严格的合法性校验,就需要配合使用 @<属性名>.setter 装饰器。

1. 编写规则与规范

  • 同名原则:Getter 方法与 Setter 方法的方法名称必须完全一致。
  • 装饰器名称:Setter 上方的装饰器语法为 @属性名.setter,这里的“属性名”必须与 Getter 方法名一致。
  • 参数个数:Setter 方法除了 self 之外,必须接收一个表示新传入数值的形参(通常命名为 value)。
  • 底层私有化存储:为了防止递归无限循环,类的实例内部通常使用带有单下划线前缀的属性名(如 _price)作为真实数据容器。
Python @property装饰器基础语法与简单示例
图 2:@setter 装饰器对属性赋值的拦截与校验执行流程

2. 商品价格校验完整代码示例

下面是一个标准的商品类,要求价格必须大于或等于 0:

class Product:
    def __init__(self, name, price):
        self.name = name
        # 在 __init__ 中也可以直接使用 self.price 触发 setter 校验
        self.price = price

    @property
    def price(self):
        """Getter: 返回底层私有字段 _price"""
        return self._price

    @price.setter
    def price(self, value):
        """Setter: 校验输入数值有效性"""
        if not isinstance(value, (int, float)):
            raise TypeError("价格必须是数值类型")
        if value < 0:
            raise ValueError("商品价格不能小于 0")
        self._price = value

# 正常创建并读取
apple = Product("苹果", 5.5)
print(f"商品: {apple.name}, 单价: {apple.price}")  # 输出示例: 商品: 苹果, 单价: 5.5

# 正常修改价格
apple.price = 6.0
print(f"新价格: {apple.price}")  # 输出示例: 新价格: 6.0

# 触发异常检验
try:
    apple.price = -10
except ValueError as e:
    print(f"赋值失败: {e}")  # 输出示例: 赋值失败: 商品价格不能小于 0

四、属性删除:@deleter 装饰器与完整生命周期

除了获取(Get)和设置(Set),Python 的属性还支持删除操作(通过 del obj.attr 触发)。如果想要拦截或控制属性的删除行为,可以使用 @<属性名>.deleter 装饰器。

当执行 del obj.attr 时,如果未定义 deleter,Python 会抛出 AttributeError: can't delete attribute;而一旦定义了 deleter,就能在该方法内完成资源清理、状态重置或释放底层变量。

Python @property装饰器基础语法与简单示例
图 3:@property、@setter 与 @deleter 构成的完整属性描述符架构

deleter 使用示例代码

class ConfigItem:
    def __init__(self, key, value):
        self.key = key
        self._value = value

    @property
    def value(self):
        return self._value

    @value.setter
    def value(self, new_val):
        self._value = new_val

    @value.deleter
    def value(self):
        print(f"正在清理属性 {self.key} 的值...")
        del self._value

item = ConfigItem("timeout", 30)
print(item.value)   # 输出示例: 30

del item.value      # 输出示例: 正在清理属性 timeout 的值...

# 再次读取已删除的属性
try:
    print(item.value)
except AttributeError as err:
    print(f"访问异常: {err}")
    # 输出示例: 访问异常: 'ConfigItem' object has no attribute '_value'

五、常见错误与编码规范

在使用 @property 的过程中,初学者常犯以下三类典型错误,需要特别注意规避:

1. 属性名冲突导致无限递归(RecursionError)

这是最常见也最致命的错误。在 setter 中直接对同名属性赋值,会导致 setter 反复自我调用,直至抛出 RecursionError: maximum recursion depth exceeded。

错误写法:
@price.setter
def price(self, value):
    self.price = value  # 错误!再次触发 price.setter,导致无限循环死锁
正确写法:
@price.setter
def price(self, value):
    self._price = value # 正确!写入底层单下划线私有变量 _price

2. 忘记在 Getter 中编写 return 语句

如果 Getter 方法体内只有计算过程但忘记写 return,访问 obj.attr 会默认得到 None。例如:

# 错误示例
@property
def fullname(self):
    val = f"{self.first_name} {self.last_name}"
    # 忘记 return val,读取 obj.fullname 将返回 None

3. 对属性化方法加上圆括号调用

方法被 @property 装饰后已转变为属性,若依然像函数一样加圆括号调用 obj.area(),Python 会报错:TypeError: 'int' object is not callable。只需直接书写 obj.area 即可。

六、全文速查与要点总结

为便于在日常开发中快速回顾与规范书写,下表与结构图总结了 @property 装饰器的核心要素与执行对照:

Python @property装饰器基础语法与简单示例
图 4:Python @property 语法规则、应用场景与高频避坑全景速查

总的来说,@property 赋予了 Python 面向对象设计极大的灵活性。在面对需要数据封装、参数校验以及动态派生字段的需求时,合理使用 @property 及其配套的 setter,能够帮助开发者构建出既健壮又符合 Pythonic 审美的高质量代码。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。