
前言类方法这个词在中文语境里含义很宽有人用它指定义在类里的所有方法有人专指classmethod。这个说法不准确容易导致概念混淆。严格说Python 里的方法按接收的第一个隐式参数分成三类实例方法instance method、类方法class methodclassmethod、静态方法static methodstaticmethod。三者解决的问题完全不同。典型误解有两个一是以为classmethod和staticmethod可以互换二是以为静态方法不能被继承。实际上两者都会被继承区别在于绑定binding时传入的是类、还是什么都不传。本文把三类方法、__init__与__new__的分工、super()的用法以及属性装饰器property一起讲清楚并给出可运行的示例。一、三类方法的区别实例方法接收self实例类方法接收cls类本身静态方法不接收隐式参数。它们的绑定行为由描述符协议descriptor protocol决定。# 适用于 Python 3.8class Demo:category demodef __init__(self, value):self.value valuedef show(self):return finstance: {self.value}classmethoddef from_string(cls, text):return cls(int(text))staticmethoddef is_positive(n):return n 0d Demo.from_string(42) # cls 自动传入 Demoprint(d.show()) # instance: 42print(Demo.is_positive(3)) # Trueprint(Demo.category) # demo方法类型装饰器第一个参数调用时自动传入常见用途实例方法无self实例操作实例状态类方法classmethodcls类备用构造器、工厂函数静态方法staticmethod无无与类相关但不依赖状态的工具函数classmethod的关键价值是子类友好cls是实际调用的那个类所以Sub.from_string(...)返回的是Sub的实例而不是写死的父类实例。# 适用于 Python 3.8class Base:def __init__(self, n):self.n nclassmethoddef build(cls, n):return cls(n)class Sub(Base):passprint(type(Sub.build(1)).__name__) # Sub二、__init__与__new__创建实例分两步__new__负责创建并返回对象__init__负责初始化已经存在的对象。__new__是静态方法虽然不写staticmethod第一个参数是cls__init__是实例方法第一个参数是self且必须返回None。# 适用于 Python 3.8class Counter:def __new__(cls, *args, **kwargs):print(__new__ 先执行)return super().__new__(cls)def __init__(self, total0):print(__init__ 后执行)self.total totalc Counter(5)print(c.total) # 5只有当__new__返回的确实是cls的实例时__init__才会被自动调用。这个细节常用于实现单例singleton或不可变类型子类比如继承tuple、int。三、super()与多继承的方法解析顺序super()不是调用父类而是沿方法解析顺序MROMethod Resolution Order找下一个类。MRO 由 C3 线性化算法生成可以通过__mro__查看。# 适用于 Python 3.8class A:def who(self):return Aclass B(A):def who(self):return B - super().who()class C(A):def who(self):return C - super().who()class D(B, C):def who(self):return D - super().who()print([cls.__name__ for cls in D.__mro__])# [D, B, C, A, object]print(D().who()) # D - B - C - A因为super()走的是 MRO 链所以在菱形继承里B.who里的super()会调用C.who而不是A.who。这既是设计亮点也是以为super()就是调父类这一误解的根源。四、property把方法伪装成属性property把一个方法变成数据描述符data descriptor因此读取obj.x会触发 getter赋值obj.x v会触发 setter。这常用于做只读属性或校验逻辑。# 适用于 Python 3.8class Temperature:def __init__(self, celsius0):self.celsius celsiuspropertydef celsius(self):return self._celsiuscelsius.setterdef celsius(self, value):if value -273.15:raise ValueError(低于绝对零度)self._celsius valuet Temperature(25)print(t.celsius) # 25t.celsius 30print(t.celsius) # 30注意属性名celsius与内部存储_celsius必须不同名否则 setter 里self.celsius value会无限递归。如果只写property不写 setter那么该属性就是只读的。五、几个常用的特殊方法特殊方法触发时机典型返回__init__实例化后必须None__repr__repr(obj)、调试输出字符串__str__str(obj)、print字符串__len__len(obj)非负整数__eq__bool或NotImplemented__hash__hash(obj)整数# 适用于 Python 3.8class Vector:def __init__(self, x, y):self.x, self.y x, ydef __repr__(self):return fVector({self.x}, {self.y})def __eq__(self, other):if not isinstance(other, Vector):return NotImplementedreturn (self.x, self.y) (other.x, other.y)print(repr(Vector(1, 2))) # Vector(1, 2)print(Vector(1, 2) Vector(1, 2)) # True__eq__返回NotImplemented注意不是NotImplementedError是让 Python 去尝试对方的__eq__这是正确处理异构类型比较的写法。常见坑点类方法里用self命名形参❌python # 适用于 Python 3.8 class A: classmethod def make(self): # 名字误导实际是 cls return self()✅python # 适用于 Python 3.8 class A: classmethod def make(cls): return cls()静态方法里访问self❌python # 适用于 Python 3.8 class A: def __init__(self): self.n 1staticmethod def get(): return self.n # NameError ✅python # 适用于 Python 3.8 class A: def __init__(self): self.n 1staticmethod def get(obj): return obj.n __init__返回非None❌python # 适用于 Python 3.8 class A: def __init__(self): return self # TypeError✅python # 适用于 Python 3.8 class A: def __init__(self): passproperty的 setter 里递归赋值❌python # 适用于 Python 3.8 class T: property def v(self): return self.vv.setter def v(self, x): self.v x # 递归RecursionError ✅python # 适用于 Python 3.8 class T: property def v(self): return self._vv.setter def v(self, x): self._v x 以为super()一定调用父类❌python # 适用于 Python 3.8 class B(A): def who(self): return A.who(self) # 写死父类多继承下会跳过中间类✅python # 适用于 Python 3.8 class B(A): def who(self): return super().who() # 沿 MRO 走__eq__返回NotImplementedError❌python # 适用于 Python 3.8 def __eq__(self, other): return NotImplementedError # 返回的是异常类恒为真✅python # 适用于 Python 3.8 def __eq__(self, other): if not isinstance(other, type(self)): return NotImplemented return self.v other.v在实例方法里改类属性却不经类名❌python # 适用于 Python 3.8 class C: total 0def inc(self): self.total 1 # 实际新建了实例属性 ✅python # 适用于 Python 3.8 class C: total 0def inc(self): type(self).total 1 总结要点结论实例方法收self操作实例状态类方法收cls适合工厂/备用构造器子类友好静态方法无隐式参数纯工具函数__new__/__init__先创建后初始化__init__必须返回Nonesuper()沿 MRO 前进不是简单调父类property数据描述符读写可分别拦截记住一条主线方法的差异本质是绑定时传什么给第一个参数。理解了描述符和 MROclassmethod、staticmethod、super()的行为就不再神秘。写工厂方法优先用cls写类型比较优先返回NotImplemented。