在 Python 中,cls 是类方法(@classmethod)的第一个参数,表示类本身而非实例。尽管 Python 不强制命名,但 PEP8 建议使用 cls。本文详细解析 cls 的核心概念、与 self 的区别、工厂方法实现,以及面试中常见的 @classmethod 与 @staticmethod 对比。
一、cls 与 self 的核心区别
self 是实例方法的第一个参数,指向当前对象实例,用于访问实例属性;cls 是类方法的第一个参数,指向当前类,用于访问类属性或调用类构造函数。两者作用域和使用场景完全不同。
二、如何定义与使用 cls
必须配合 @classmethod 装饰器,参数名约定为 cls(可改名但不建议)。示例:- class Person:
- species = "Human" # 类属性
- def __init__(self, name):
- self.name = name
- @classmethod
- def create_anonymous(cls):
- # cls 指向 Person,等价于 Person("Anonymous")
- return cls("Anonymous")
- @classmethod
- def get_species(cls):
- return cls.species
- p = Person.create_anonymous()
- print(p.name) # 输出: Anonymous
- print(Person.get_species()) # 输出: Human
复制代码 调用类方法时,Python 自动将当前类传入 cls 参数,无需手动传递。
三、最核心的应用场景:工厂方法
通过 cls 可以实现多种构造方式,并且支持多态继承——子类调用父类工厂方法时,cls 自动指向子类,无需重写任何代码。示例:- class DateTime:
- def __init__(self, year, month, day):
- self.year = year
- self.month = month
- self.day = day
- @classmethod
- def from_string(cls, date_str):
- y, m, d = map(int, date_str.split('-'))
- return cls(y, m, d)
- class LogTime(DateTime):
- pass
- log = LogTime.from_string("2026-07-15")
- print(type(log)) # <class '__main__.LogTime'>
复制代码 这里 from_string 是工厂方法,子类 LogTime 调用时 cls 自动变为 LogTime,返回 LogTime 实例,完美支持多态。
四、面试易错点:@classmethod vs @staticmethod
- @classmethod 必须接收 cls 参数,可以访问/修改类属性,并能被子类正确继承(cls 自动指向子类)。
- @staticmethod 不需要 self 或 cls,只是放在类命名空间下的普通函数,无法访问类或实例属性。
示例:- class Test:
- @staticmethod
- def static_method():
- return "我不需要cls"
复制代码 总结:cls 是“类的替身”,用于在类方法中操作类属性或灵活创建本类(及子类)实例。当需要扩展 __init__ 提供替代构造函数时,@classmethod 是最佳选择。 |