Decorator -- property
@property 可以把一个 方法 伪装成一个 只读属性(像字段一样访问)。
核心功能:
- 属性访问 -> 调用 getter 方法
- 使 API 更优雅
- 用于“计算型字段”“包装真实字段”“提供受控访问”
class User:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name
使用:
u = User("Jack")
print(u.name) # 像属性访问,但背后实际是调用方法
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)
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
例如股票涨停价是浮动计算出来的,不允许直接修改。
例如密码 hash。
你项目里就很多,比如:
- datetime 字符串转换成 date
- 股票代码格式转换
- 自动生成全名 full_name
- 自动格式化股票涨跌幅百分比
更好的 API 设计。
你经常使用 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)
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 化。
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())
等价于:
name = property(fget, fset, fdel)
也就是说:
- 不加 setter -> 不可写(只读)
- 不加 deleter -> 不可删除
- ❌ property 不应该做复杂逻辑(会导致意外性能问题)
- ❌ property 不应该有明显副作用(不然访问属性就改变对象,很诡异)
- ❌ property 不适合做耗时操作(比如数据库查询)
✅ 推荐用于 “轻量计算字段”
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"
| 用途 | 是否适合 property | | 只读属性 | ✅ | | 简单计算字段 | ✅ | | 私有字段包装 | ✅ | | 数据验证 | ✅(在 setter 中) | | 复杂逻辑 | ❌ | | 耗时操作(网络 / DB) | ❌ | | 会引起副作用 | ❌ |