Decorator -- staticmethod / classmethod / property 的超强对比表
| 特性 | 普通方法 | @staticmethod | @classmethod | @property |
|---|---|---|---|---|
| 自动注入参数 | self(实例) | 无自动参数 | cls(类) | self(实例) |
| 能否访问实例属性 | ✅ | ❌ | ❌ | ✅ |
| 能否访问类属性 | ✅ | ❌ | ✅ | ✅ |
| 是否依赖实例 | ✅ 强依赖 | ❌ 完全不依赖 | ❌ 不依赖实例 | ✅ 获取实例状态 |
| 调用方式 | 实例调用 | 类 / 实例均可 | 类 / 实例均可 | 实例属性访问方式 |
| 典型用途 | 操作实例数据 | 工具函数、转换函数 | 工厂方法、配置、类级别操作 | 将方法伪装成“属性” |
| 是否支持 setter | ❌ | ❌ | ❌ | ✅ 可做属性可写化 |
| 是否支持继承多态 | ✅ | ✅(但少用) | ✅(常见,用于子类构造) | ✅ |
| 设计意图 | 行为依赖对象状态 | 与类逻辑相关但不依赖类/实例 | 以“类”为核心逻辑 | 将 getter 方法包装成字段 |
终极记忆口诀
- self -> 普通方法
- cls -> classmethod
- nothing -> staticmethod
- 属性接口 -> property
完全与实例无关,只是被“放在类里面的工具函数”。
方法围绕“类”本身展开,通常用于创建对象、加载配置、适配子类。
把方法变成字段一样访问,实现干净优雅的 API。
用于转换、校验、格式化等逻辑。
class Math:
@staticmethod
def add(a, b):
return a + b
使用:
Math.add(1, 2)
不需要类,也不需要实例。
例如从 JSON 创建对象:
class User:
def __init__(self, name):
self.name = name
@classmethod
def from_dict(cls, data):
return cls(data["name"])
支持子类:
class Admin(User):
pass
a = Admin.from_dict({"name": "root"})
print(type(a)) # <class '__main__.Admin'>
class User:
def __init__(self, firstname, lastname):
self.firstname = firstname
self.lastname = lastname
@property
def fullname(self):
return f"{self.firstname} {self.lastname}"
像字段一样访问:
print(user.fullname)
而不是:
print(user.fullname())
class Demo:
class_level_value = 100
def __init__(self, x):
self.x = x
def normal(self):
return self.x * 2
@staticmethod
def util(a, b):
return a + b
@classmethod
def make(cls, y):
return cls(y)
@property
def doubled(self):
return self.x * 2
调用:
d = Demo(10)
d.normal() # 20
Demo.util(1, 2) # 3
Demo.make(99) # 创建 Demo 或其子类对象
d.doubled # 20(注意:没有括号)
| 场景 | 用什么 | 原因 | | 方法逻辑与实例有关 | 普通方法 | 需要 self | | 工具函数、数据转换、格式化 | staticmethod | 独立逻辑、放类里更组织化 | | 基于类创建对象(工厂) | classmethod | 返回 cls,可兼容子类 | | 加载配置/初始化环境 | classmethod | 逻辑属于类本身 | | 将 getter “伪装成属性” | property | 让 API 更优雅 | | 需要可写属性 | @property + @setter | 控制写入逻辑 | | 完全无关类、应该放 utils | 顶级函数或 staticmethod | 避免污染类职责 |
你 FastAPI + PostgreSQL 项目建议这样:
utils/
├── string.py
├── validate.py
├── date.py
├── stock.py
├── crypto.py
类里面使用组合:
- 逻辑独立 -> @staticmethod
- 可能支持子类扩展 -> @classmethod
- 数据对象计算属性 -> @property
这样你的 utils 模块会非常清晰。