资讯动态

Python标准库中内置装饰器@staticmethod和@classmethod

发布时间:2026/10/11 11:52:38 来源:尧图企业网站定制
前言staticmethod和classmethod是 Python内置built-in的两个类型它们可以当装饰器用写在方法定义上方。它们的共同点是改变方法「第一个参数怎么来」的规则。三个常见误解先说清楚把classmethod当成 Java/C 里的静态方法。不是。classmethod收的第一个隐式参数是类本身习惯叫cls它和 C/Java 的静态方法完全不同Python 里对应 C/Java 静态方法的是staticmethod。以为staticmethod写出来的函数「就不属于这个类了」。它仍然是类的一个属性只是通过类和实例访问时都不做绑定谁也不会被自动塞进来。以为这两个只是「语法标记」没有运行时对象。实际上类字典里存的是staticmethod/classmethod对象它们是描述符descriptor在属性访问时才决定返回函数还是绑定方法。另外提醒一句本文所有示例都按 Python 3 书写。Python 2.7 已经在2020 年 1 月 1 日停止维护Python 2 里super(C, self)那种必须显式传类与实例的写法在 3 里直接写super()就行不要再照抄老代码。一、三种方法对比# 适用于 Python 3.8class Demo:count 0def instance_method(self, x):return finstance: {x}classmethoddef class_method(cls, x):return fclass: {cls.__name__}, {x}staticmethoddef static_method(x):return fstatic: {x}d Demo()print(d.instance_method(1)) # instance: 1print(Demo.instance_method(d, 1)) # instance: 1print(Demo.class_method(2)) # class: Demo, 2print(d.class_method(2)) # class: Demo, 2print(Demo.static_method(3)) # static: 3print(d.static_method(3)) # static: 3类型第一个隐式参数通过实例调用通过类调用实例方法self实例自动传实例要手动传实例classmethodcls类自动传类自动传类staticmethod无不传任何东西不传任何东西关键差别在「通过实例调用」这一列d.class_method()传进去的是Demo这个类而不是实例d。所以类方法拿不到实例数据只能访问类级别的数据。二、classmethod 的典型用途用途一替代构造函数工厂方法。这是类方法最经典的用法。写from_xxx这类方法让调用方用不同的输入形式创建对象# 适用于 Python 3.8class Point:def __init__(self, x, y):self.x xself.y yclassmethoddef from_string(cls, text):# cls 是「当前被调用的那个类」不是写死的 Pointa, b text.split(,)return cls(float(a), float(b))def __repr__(self):return fPoint({self.x}, {self.y})print(Point.from_string(1.5,2.5)) # Point(1.5, 2.5)用途二让子类得到正确的类型。因为cls是运行时传入的子类调用from_string时得到的会是子类实例而不是父类实例# 适用于 Python 3.8class Point3D(Point):def __init__(self, x, y, z0.0):super().__init__(x, y)self.z zp Point3D.from_string(1,2)print(type(p).__name__) # Point3D如果把from_string里的cls(...)写成Point(...)这里就会退化成Point这就是「用cls而不是类名」的意义。用途三访问或修改类级别的配置。例如注册表模式、统计类的实例数量等这些状态挂在类上用类方法读写最自然。三、staticmethod 的典型用途静态方法和普通模块级函数几乎一样唯一区别是它挂在类的命名空间下。适合这么用的场景这段逻辑在概念上属于这个类但不依赖实例状态也不依赖类状态你希望调用方能按ClassName.helper(...)的方式找到它而不是满世界找模块函数。# 适用于 Python 3.8class Temperature:staticmethoddef c_to_f(celsius):return celsius * 9 / 5 32staticmethoddef f_to_c(fahrenheit):return (fahrenheit - 32) * 5 / 9print(Temperature.c_to_f(100)) # 212.0这两个函数没有用到self也没有用到cls写成静态方法就把「单位换算」这件事归到了Temperature名下。还有一种是必须用静态方法的场景你想在类体里引用一个函数但又不想让它被自动变成实例方法。这时可以写成method staticmethod(regular_function)官方文档专门提到了这个写法。四、版本差异容易记错的地方这两个装饰器的行为在 3.x 内部改过写文章或读老代码时要分清楚Python 3.9classmethod可以包裹其他描述符比如property()当时可以写出「类级别的只读属性」。Python 3.10staticmethod和classmethod开始继承方法属性——__module__、__name__、__qualname__、__doc__、__annotations__并且新增了__wrapped__属性同时静态方法变成了可像普通函数一样调用所以在类体里直接调用静态方法如f()在 3.10 起才成立。Python 3.11 起弃用、3.13 起移除classmethod不能再包裹property()这类描述符。最后一条很关键如果你在网上看到classmethod叠property写「类属性」的写法那是 3.9 到 3.12 之间短暂存在的用法在 3.13 及以后会失败。类级别的计算属性现在应该用普通类方法 调用或者用元类metaclass上的property来实现别再照抄那个组合。五、一个完整示例# 适用于 Python 3.8class User:_registry {}def __init__(self, name, roleuser):self.name nameself.role roleclassmethoddef register(cls, name, roleuser):user cls(name, role)cls._registry[name] userreturn userclassmethoddef get(cls, name):# cls._registry 在子类里会解析到子类自己的注册表return cls._registry.get(name)staticmethoddef valid_name(name):return bool(name) and name.isidentifier()def __repr__(self):return fUser({self.name!r}, role{self.role!r})if __name__ __main__:print(User.valid_name(ann)) # Trueprint(User.valid_name(1ann)) # FalseUser.register(ann, admin)User.register(bob)print(User.get(ann)) # User(ann, roleadmin)print(sorted(User._registry)) # [ann, bob]valid_name用静态方法因为它是纯函数register/get用类方法因为它们操作类级字典_registry__init__保持实例方法因为它要初始化实例。常见坑点把classmethod当 C/Java 的静态方法❌ 在classmethod里写def f():少写cls调用时报参数错误 ✅def f(cls):要的是 C/Java 静态方法语义就用staticmethod在类方法里用self❌classmethod里写self.x第一个参数其实是类没有实例属性 ✅ 想访问实例数据就别用类方法类方法只能碰类级别的属性工厂方法里写死类名❌return Point(a, b)子类调用后得到的是父类实例 ✅return cls(a, b)让子类拿到自己的类型在类方法里读实例属性❌cls.name却期望拿到某个实例的name✅ 类方法拿到的是类对象实例数据要从参数里显式传进来以为静态方法会收到self❌staticmethod def f(self):然后d.f()期望self有值 ✅ 静态方法不绑定任何东西参数表里有什么就得传什么在 3.13 及以后还写classmethodproperty❌ 3.9 起可用、3.11 弃用、3.13 移除照抄会报错 ✅ 类级别计算属性改用普通类方法或元类上的property在类体里直接调用静态方法却用的是 3.9❌ 3.9 及更早在类体里写f()调用静态方法会失败 ✅ 静态方法可当普通函数调用是3.10 起的行为旧版本写成模块级函数把类方法当成装饰实例方法用❌classmethod加在需要实例状态的save(self)上 ✅ 只有不依赖实例状态的方法才适合做成类方法或静态方法总结关注点classmethodstaticmethod隐式第一个参数cls类对象无能否访问实例数据不能不能常见用途替代构造函数、类级配置、注册表概念上属于类的纯函数派生类调用时传入派生类无差别3.10 起新增__name__等属性 __wrapped__同上且可作为普通函数调用3.13 起不能再包裹property无此限制选择标准可以简化成一句话方法需要实例数据就用普通实例方法需要类本身尤其是为了继承时正确地拿到子类就用classmethod两者都不需要就用staticmethod。把这条标准放在心里绝大多数设计上的犹豫都能立刻定下来。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑