python中的属性装饰器@property用法和理解

@property 是个非常有用的装饰器,它让我们可以基于方法定义类

属性,精确地控制属性的读取、赋值和删除行为,灵活地实现动态属

性等功能。

  • 方法(method)表示行为,需要用 () 调用 ,比如 obj.method()。
  • 属性(attribute)表示状态,可以直接访问 ,比如 obj.attr。
  • @property 装饰器让方法看起来像属性 ,这样就能像 obj.attr 这样使用它,而不需要 ()。
  • 但要注意,不是所有方法都适合变成属性 ,尤其是那些计算量大或者需要网络请求的方法 ,因为用户一般期望读取属性时是"秒出"的。

🌟 举个简单的例子

方法和属性的区别:

python 复制代码
class Person:
    def __init__(self, name, birth_year):
        self.name = name
        self.birth_year = birth_year

    def get_age(self):  # 这是方法
        """计算年龄"""
        current_year = 2024
        return current_year - self.birth_year

p = Person("Alice", 1990)
print(p.get_age())  # 34(需要用 () 调用)

📌 get_age() 是方法,因为它是一个行为(计算年龄),调用时需要 ()。


🌟 用 @property 让方法变成属性

python 复制代码
class Person:
    def __init__(self, name, birth_year):
        self.name = name
        self.birth_year = birth_year

    @property
    def age(self):  # 变成了属性
        """计算年龄"""
        current_year = 2024
        return current_year - self.birth_year

p = Person("Alice", 1990)
print(p.age)  # 34(现在不用加 () 了!)

📌 age 变成了"伪装的属性",看起来像 p.age,但其实是一个方法。


🌟 再看 setter 和 deleter

用 @property 装饰后,我们还可以加 @setter 和 @deleter,让属性支持修改和删除:

python 复制代码
class Person:
    def __init__(self, name, birth_year):
        self.name = name
        self.birth_year = birth_year

    @property
    def age(self):
        return 2024 - self.birth_year

    @age.setter
    def age(self, new_age):
        """通过修改 age 来调整 birth_year"""
        self.birth_year = 2024 - new_age

    @age.deleter
    def age(self):
        """禁止删除 age"""
        raise RuntimeError("Can't delete age!")

p = Person("Alice", 1990)
print(p.age)  # 34

p.age = 40  # 触发 setter,修改 birth_year
print(p.birth_year)  # 1984

del p.age  # 触发 deleter,抛出异常
# RuntimeError: Can't delete age!

📌 setter 让 p.age = 40 可以工作 ,实际上它是修改 birth_year。

📌 deleter 阻止 del p.age,防止错误操作。


🌟 什么时候不该用 @property?

如果方法计算量很大,或者涉及网络请求、数据库查询等操作 ,就不应该用 @property,否则会让用户误以为它是个普通属性,而读取普通属性通常是"瞬间"的。

❌ 不好的例子:

python 复制代码
class DataFetcher:
    @property
    def latest_data(self):
        """从 API 获取最新数据(可能要 5 秒!)"""
        import time
        time.sleep(5)  # 模拟慢速 API
        return "Latest Data"

df = DataFetcher()
print(df.latest_data)  # 需要等 5 秒

📌 这样设计不合理,因为 df.latest_data 读起来像是普通属性,但它实际上很慢!

📌 更好的做法:

python 复制代码
class DataFetcher:
    def get_latest_data(self):
        """从 API 获取最新数据"""
        import time
        time.sleep(5)  # 模拟慢速 API
        return "Latest Data"

df = DataFetcher()
print(df.get_latest_data())  # 需要等 5 秒,但用户知道它是个方法

📌 方法 df.get_latest_data() 需要 (),能让用户意识到它可能很慢。


🌟 结论

  • @property 可以把方法变成属性,让代码更优雅 ,比如 p.age。
  • 可以加 @setter 和 @deleter 让属性支持赋值和删除。
  • 但计算量大、网络请求等方法 ❌ 不适合用 @property,否则会让人误解。

开发过程中会用到的场景

🌟 实际开发中 @property 的应用场景

在开发中,@property 常用于封装逻辑,让类的属性更直观。以下是几个实际应用场景:


1️⃣ 计算属性(自动计算值)

💡 场景 :

在交易系统中,Order 类有 unit_price(单价)和 quantity(数量),我们想让 total_price(总价)作为一个属性,而不是方法。

python 复制代码
class Order:
    def __init__(self, unit_price, quantity):
        self.unit_price = unit_price
        self.quantity = quantity

    @property
    def total_price(self):
        """计算总价"""
        return self.unit_price * self.quantity

order = Order(100, 5)
print(order.total_price)  # 500(自动计算)

📌 为什么用 @property?

  • 让 order.total_price 看起来就像普通属性,而不用 order.total_price(),更加直观。
  • 自动计算,无需手动更新 total_price。

2️⃣ 限制属性修改(只读属性)

💡 场景 :

有时候,我们想让某个属性只能读取,不能修改 ,比如 User 类的 email。

python 复制代码
class User:
    def __init__(self, name, email):
        self.name = name
        self._email = email  # 保护属性,外部不应直接修改

    @property
    def email(self):
        """只读属性,防止外部修改"""
        return self._email

user = User("Alice", "alice@example.com")
print(user.email)  # alice@example.com

user.email = "bob@example.com"  # ❌ 不能修改
# AttributeError: can't set attribute 'email'

📌 为什么用 @property?

  • 让 email 变成"只读"属性,防止外部修改。
  • 如果直接 user._email,仍然可以修改,但加 @property 能起到一定的约束作用。

3️⃣ 属性值格式化(自动处理数据格式)

💡 场景 :

在用户系统中,phone_number 可能有不同的格式(+86, 0086 等),我们可以用 @property 让它始终返回标准格式。

python 复制代码
class User:
    def __init__(self, name, phone):
        self.name = name
        self._phone = phone

    @property
    def phone(self):
        """格式化手机号,确保前面有 +86"""
        if not self._phone.startswith("+86"):
            return "+86 " + self._phone
        return self._phone

user = User("Alice", "13800138000")
print(user.phone)  # +86 13800138000(自动加上 +86)

📌 为什么用 @property?

  • 让 user.phone 始终返回标准格式,外部使用时不需要关心格式问题。

4️⃣ 动态属性更新(修改一个属性影响另一个属性)

💡 场景 :

一个商品折扣系统 ,price(原价)和 discount(折扣)变化时,final_price(最终价格)需要自动更新。

python 复制代码
class Product:
    def __init__(self, name, price, discount=0):
        self.name = name
        self.price = price
        self.discount = discount  # 折扣(百分比)

    @property
    def final_price(self):
        """计算折扣后的最终价格"""
        return self.price * (1 - self.discount / 100)

product = Product("Laptop", 10000, 10)
print(product.final_price)  # 9000.0(自动计算折扣价)

product.discount = 20
print(product.final_price)  # 8000.0(折扣变化,价格自动更新)

📌 为什么用 @property?

  • final_price 不需要存储,每次调用自动计算,保证数据一致性。
  • 只要 discount 变化,final_price 自动更新,外部不需要手动计算。

5️⃣ 设置属性时触发额外逻辑

💡 场景 :

用户修改密码时,要自动加密,防止存储明文密码。

python 复制代码
import hashlib

class User:
    def __init__(self, username, password):
        self.username = username
        self._password = self._encrypt(password)

    def _encrypt(self, password):
        """简单哈希加密"""
        return hashlib.sha256(password.encode()).hexdigest()

    @property
    def password(self):
        """密码是敏感信息,不允许读取"""
        raise ValueError("Password is write-only!")

    @password.setter
    def password(self, new_password):
        """用户修改密码时自动加密"""
        self._password = self._encrypt(new_password)

user = User("Alice", "mypassword")
print(user._password)  # 存的是加密后的密码

user.password = "newpassword"  # 自动加密
print(user._password)  # 新的加密密码

print(user.password)  # ❌ 不能读取密码
# ValueError: Password is write-only!

📌 为什么用 @property?

  • 禁止明文存储密码,避免安全问题。
  • password 是"写入时加密",但不能读取,防止信息泄露。

总结

应用场景 使用方式 示例代码
计算属性 让属性动态计算而不是存储 total_price = unit_price * quantity
只读属性 防止外部修改某些属性 @property def email(self): return self._email
格式化属性 让属性值自动调整格式 phone_number 自动补 +86
动态属性更新 修改一个属性时,另一个自动变化 discount 变化时 final_price 自动更新
写入时触发逻辑 设置属性时自动加密或转换 password 赋值时自动加密

🌟 什么时候适合用 @property?

✅ 适合:

  • 需要 动态计算 而不是存储的属性(如 total_price)。
  • 需要 限制外部修改 的属性(如 email)。
  • 需要 数据格式化 的属性(如 phone_number)。
  • 需要 写入时触发额外逻辑 (如 password 自动加密)。

❌ 不适合:

  • 计算量大,或者涉及数据库/网络请求 的方法,比如 fetch_data(),否则每次访问都会触发耗时操作。
相关推荐
张张123y4 小时前
后端第一课:输入 URL 后发生了什么?我用手写 Socket 把全链路跑通了
python·websocket·ai·socket·url·后端开发
白帽攻防录6 小时前
SRC 挖洞:PostScript 打印机驱动双 RCE,打印服务器怎么变成内网跳板
开发语言·网络安全·php·内网安全
十年Java程序媛6 小时前
Java枚举Enum完整教程|替代静态常量,开发与面试要点
java·开发语言·spring boot·后端·面试
全栈弄潮儿6 小时前
Python实战第7期:字典与集合
后端·python·编程语言
2603_965148116 小时前
电商选品调研:通过关键词搜索API获取市场热销商品,结合商品详情API分析竞品卖点与定价
开发语言·数据库·人工智能·python·信息可视化
xixiaoyunya7 小时前
Python 异步编程入门:用 asyncio 轻松实现并发任务
开发语言·python·php
把我格子衫拿来7 小时前
Dice 很高就够了吗?分割评价中的 IoU、表面距离与 HD95
python·图像分割·模型评估·医学影像·dice
学逆向的7 小时前
C 循环结构的执行效率分析
c语言·开发语言·数据结构·网络安全
曲径海盗8 小时前
【Python 学习第 31 天】复习:变量与数据类型 等 3 题
python·学习笔记·编程入门
Data_Journal8 小时前
如何绕过 Cloudflare:网页抓取的最佳方法
网络·python