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,但其实是一个方法


🌟 再看 setterdeleter

@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!

📌 setterp.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(),否则每次访问都会触发耗时操作。
相关推荐
m0_748245344 分钟前
python——Django 框架
开发语言·python·django
曼巴UE58 分钟前
UE5.3 C++ TArray系列(一)
开发语言·c++·ue5
熬夜苦读学习20 分钟前
Linux文件系统
linux·运维·服务器·开发语言·后端
菜鸟一枚在这28 分钟前
深度解析建造者模式:复杂对象构建的优雅之道
java·开发语言·算法
java1234_小锋31 分钟前
一周学会Flask3 Python Web开发-客户端状态信息Cookie以及加密
前端·python·flask·flask3
阿巴~阿巴~1 小时前
多源 BFS 算法详解:从原理到实现,高效解决多源最短路问题
开发语言·数据结构·c++·算法·宽度优先
B站计算机毕业设计超人1 小时前
计算机毕业设计Python+DeepSeek-R1高考推荐系统 高考分数线预测 大数据毕设(源码+LW文档+PPT+讲解)
大数据·python·机器学习·网络爬虫·课程设计·数据可视化·推荐算法
winfredzhang2 小时前
Python实战:Excel中文转拼音工具开发教程
python·安全·excel·汉字·pinyin·缩写
奔跑吧邓邓子2 小时前
【Python爬虫(34)】Python多进程编程:开启高效并行世界的钥匙
开发语言·爬虫·python·多进程
Heris992 小时前
2.22 c++练习【operator运算符重载、封装消息队列、封装信号灯集】
开发语言·c++