@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(),否则每次访问都会触发耗时操作。