classmethod的第一个参数必须是cls,因为Python解释器会自动将调用该方法的类对象作为第一个参数传入,缺省即报TypeError;cls是位置强制、不可省略的约定参数,用于访问类属性和实现工厂方法等类级别操作。

classmethod 的第一个参数为什么必须是 cls
因为 @classmethod 的本质是让 Python 自动把「调用该方法的类对象」作为第一个参数传进来,而 cls 只是一个约定俗成的名字——你写成 klass 或 clazz 也能运行,但必须存在且是第一个位置。
这个参数不是可选的,也不是装饰器“建议”你加的;它是 Python 解释器在调用类方法时强制注入的。不写,就会报 TypeError: xxx() missing 1 required positional argument: 'cls'。
不写 cls 会出什么错
常见错误现象:定义了 @classmethod,但参数列表里漏了 cls,比如:
class User:
@classmethod
def create(username, password): # ❌ 缺少 cls
return User(username=username, password=password)
调用 User.create("alice", "123") 时,Python 实际上传入的是 User, "alice", "123" —— 三个参数,但函数只声明了两个形参,于是触发:
立即学习“Python免费学习笔记(深入)”;
TypeError: create() takes 2 positional arguments but 3 were given
更隐蔽的情况是混用关键字参数:
- 定义为
def register(username=None, password=None)(无cls) - 调用为
User.register(username="a", password="b") - 结果报
TypeError: register() got multiple values for argument 'username'
原因:解释器把 User 当作第一个隐式参数塞进去,又通过关键字给了 username,导致重复赋值。
cls 和 self 的根本区别在哪
区别不在名字,而在绑定对象的类型和生命周期:
-
self绑定的是实例对象,它的属性、方法调用都依赖具体实例的状态 -
cls绑定的是类对象,它能访问cls.attr、调用其他@classmethod或@staticmethod,甚至做cls()实例化 - 继承时差异最明显:子类调用父类的
@classmethod,cls是子类本身,不是父类 —— 这正是工厂方法能正确返回子类实例的关键
比如:
class Animal:
@classmethod
def spawn(cls):
return cls() # 注意:这里返回的是调用者的类,不是 Animal
<p>class Dog(Animal):
pass</p><p>d = Dog.spawn() # d 是 Dog() 实例,不是 Animal()
可以改名吗?会影响什么
可以,但没必要。只要它是第一个参数,名字任意,例如:
@classmethod
def load(kls, data): # ✅ 合法,kls 就是 cls
return kls(**data)
但要注意:
- 所有团队成员都得认这个新名字,协作成本上升
- IDE 和静态检查工具(如 mypy)默认按
cls做类型推导,换名可能触发误报 - 文档和注释若仍写 “cls”,会造成理解断层
真正容易被忽略的是:即使你写了 cls,如果把它放在非首位(比如加了 *args 在前),一样会报错 —— 位置比名字重要。


















