Skip to main content
Documents
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Decorator -- property

🚀 1. property 是什么?

@property 可以把一个 方法 伪装成一个 只读属性(像字段一样访问)。

核心功能:

  • 属性访问 -> 调用 getter 方法
  • 使 API 更优雅
  • 用于“计算型字段”“包装真实字段”“提供受控访问”

📌 2. 最简单的示例

class User:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

使用:

u = User("Jack")
print(u.name)   # 像属性访问,但背后实际是调用方法

📌 3. 添加 setter(可写属性)

class User:
    def __init__(self):
        self._age = 0

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if value < 0:
            raise ValueError("年龄不能为负数")
        self._age = value

使用:

u = User()
u.age = 18       # 自动调用 setter
print(u.age)

📌 4. 添加 deleter(可删除属性逻辑)

class User:
    def __init__(self):
        self._token = "abc123"

    @property
    def token(self):
        return self._token

    @token.deleter
    def token(self):
        self._token = None

使用:

u = User()
del u.token

📌 5. 什么时候使用 property?

想让属性只读

例如股票涨停价是浮动计算出来的,不允许直接修改。

想保护真实字段(私有变量)

例如密码 hash。

想封装计算字段

你项目里就很多,比如:

  • datetime 字符串转换成 date
  • 股票代码格式转换
  • 自动生成全名 full_name
  • 自动格式化股票涨跌幅百分比

想优雅访问复杂逻辑

更好的 API 设计。

📌 6. 你项目中 property 的真实实战示例

6.1 股票代码转换(强烈推荐!)

你经常使用 stock.coderename(code)

可以改成 计算型属性:

class Stock:
    def __init__(self, code):
        self.raw_code = code

    @property
    def formatted(self):
        """返回 sz000001 / sh600000 格式"""
        if self.raw_code.startswith("00"):
            return "sz" + self.raw_code
        if self.raw_code.startswith("60"):
            return "sh" + self.raw_code
        return self.raw_code

使用:

s = Stock("600001")
print(s.formatted)  

6.2 自动格式化日期(数据入库很常用)

class News:
    def __init__(self, time_str):
        self._time_str = time_str

    @property
    def date(self):
        return datetime.strptime(self._time_str, "%Y-%m-%d %H:%M:%S").date()

你现在很多地方用 utils.datetime_string_to_datetime,完全可以 property 化。

6.3 保护敏感字段(如密码)

class User:
    def __init__(self, password):
        self._password = bcrypt.hashpw(password.encode(), bcrypt.gensalt())

    @property
    def password(self):
        raise AttributeError("不允许直接读取密码")

    @password.setter
    def password(self, raw_pwd: str):
        self._password = bcrypt.hashpw(raw_pwd.encode(), bcrypt.gensalt())

📌 7. property 的内部机制(你一定要知道)

等价于:

name = property(fget, fset, fdel)

也就是说:

  • 不加 setter -> 不可写(只读)
  • 不加 deleter -> 不可删除

📌 8. 避免滥用 property(经验总结)

  • ❌ property 不应该做复杂逻辑(会导致意外性能问题)
  • ❌ property 不应该有明显副作用(不然访问属性就改变对象,很诡异)
  • ❌ property 不适合做耗时操作(比如数据库查询)

✅ 推荐用于 “轻量计算字段”

📌 9. property + dataclass 的最佳组合

from dataclasses import dataclass

@dataclass
class Stock:
    code: str
    name: str

    @property
    def market(self):
        if self.code.startswith("00"):
            return "SZ"
        if self.code.startswith("60"):
            return "SH"
        return "OTHER"

📌 10. 总结(你可以收藏的最终表)

| 用途 | 是否适合 property | | 只读属性 | ✅ | | 简单计算字段 | ✅ | | 私有字段包装 | ✅ | | 数据验证 | ✅(在 setter 中) | | 复杂逻辑 | ❌ | | 耗时操作(网络 / DB) | ❌ | | 会引起副作用 | ❌ |