在 Python 中,self 和 cls 分别是实例方法 和类方法的第一个参数。它们虽然名字不同,但都是一种约定俗成的命名,用来接收调用时自动传入的对象。下面详细讲解它们的异同、优劣以及使用场景。
1. 什么是 self 和 cls?
self:出现在实例方法 中,代表当前实例对象(即通过实例调用方法时,该实例本身)。cls:出现在类方法 中(使用@classmethod装饰),代表当前类对象(即通过类或实例调用时,类本身)。
示例定义:
python
class MyClass:
class_attr = "我是类属性"
def instance_method(self):
print("这是实例方法,self 是:", self)
@classmethod
def class_method(cls):
print("这是类方法,cls 是:", cls)
调用:
python
obj = MyClass()
obj.instance_method() # self 就是 obj
MyClass.class_method() # cls 就是 MyClass
obj.class_method() # 即使通过实例调用,cls 仍然是 MyClass
输出:
这是实例方法,self 是: <__main__.MyClass object at 0x...>
这是类方法,cls 是: <class '__main__.MyClass'>
这是类方法,cls 是: <class '__main__.MyClass'>
2. 相同点
- 都是方法的第一个参数:Python 会自动将调用者(实例或类)作为第一个参数传入。
- 都可以访问类属性 :实例方法可以通过
self.__class__.class_attr或self.class_attr(若实例没有同名属性)访问类属性;类方法直接通过cls.class_attr访问。 - 都可以被继承 :子类会继承父类的实例方法和类方法,且
self/cls会根据实际调用者改变(多态)。
3. 不同点
| 对比项 | 实例方法(self) | 类方法(cls) |
|---|---|---|
| 装饰器 | 不需要 | 需要 @classmethod |
| 第一个参数代表什么 | 实例对象 | 类对象 |
| 能否访问实例属性 | ✅ 可以(self.attr) |
❌ 不可以(没有 self,cls 是类,无法知道具体实例) |
| 能否修改实例状态 | ✅ 可以 | ❌ 不可以 |
| 能否访问/修改类属性 | ✅ 可以(通过 self.__class__ 或 self 如果实例没有同名属性) |
✅ 可以(直接 cls.attr) |
| 调用方式 | 通常通过实例调用,也可以通过类调用(需手动传实例) | 可以通过类调用,也可以通过实例调用(但 cls 始终是类) |
| 典型用途 | 操作对象自身的数据和行为 | 操作类级别的数据、工厂方法、继承时定制行为 |
4. 各自优势与适用场景
4.1 实例方法(self)的优势
- 直接操作实例状态:适合处理每个对象特有的数据。
- 符合面向对象封装:将数据和操作数据的方法绑定在一起。
适用场景:
- 修改或读取实例属性。
- 调用其他实例方法。
示例:
python
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
print(f"{self.name} 在汪汪叫!")
dog = Dog("旺财")
dog.bark() # 输出:旺财 在汪汪叫!
4.2 类方法(cls)的优势
- 不依赖实例:即使没有创建实例,也可以通过类名调用。
- 操作类属性:适合修改或读取属于类本身的数据。
- 实现多态 :当子类继承并调用类方法时,
cls会指向子类,从而可以针对子类定制行为。 - 工厂方法:常用于根据参数创建不同子类的实例。
适用场景:
- 定义备选构造器(如
from_json、from_config)。 - 修改类属性(如计数器、配置)。
- 在继承体系中,子类需要定制父类方法的行为。
示例1:操作类属性
python
class Car:
total_cars = 0
def __init__(self):
Car.total_cars += 1 # 也可以通过 self.__class__.total_cars += 1
@classmethod
def get_total_cars(cls):
return cls.total_cars
print(Car.get_total_cars()) # 0
car1 = Car()
car2 = Car()
print(Car.get_total_cars()) # 2
示例2:工厂方法 + 多态
python
class Animal:
@classmethod
def create(cls, name):
return cls(name) # cls 会根据调用者变成子类
class Dog(Animal):
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} 汪汪"
class Cat(Animal):
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} 喵喵"
dog = Dog.create("旺财") # 调用 Animal.create,但 cls 是 Dog
print(dog.speak()) # 旺财 汪汪
cat = Cat.create("咪咪")
print(cat.speak()) # 咪咪 喵喵
5. 综合对比示例
python
class Person:
species = "人类" # 类属性
def __init__(self, name):
self.name = name # 实例属性
# 实例方法
def introduce(self):
print(f"我是 {self.name},属于{self.species}") # 可以访问实例和类属性
# 类方法
@classmethod
def change_species(cls, new_species):
cls.species = new_species # 修改类属性
# 使用实例方法
p1 = Person("张三")
p1.introduce() # 我是 张三,属于人类
# 使用类方法(直接通过类调用)
Person.change_species("新人类")
p2 = Person("李四")
p2.introduce() # 我是 李四,属于新人类
# 通过实例调用类方法(不推荐,但可以)
p1.change_species("超人类")
p2.introduce() # 我是 李四,属于超人类
6. 总结:如何选择?
| 需求 | 选择 |
|---|---|
| 需要访问或修改实例属性 | 使用实例方法(self) |
| 需要访问或修改类属性 | 可以使用类方法(cls),也可以用实例方法通过 self.__class__,但类方法更清晰 |
| 不需要访问实例属性,但需要利用类的多态性 | 使用类方法 |
| 逻辑与类相关,但与实例无关,也不想受继承影响 | 可以使用静态方法(@staticmethod),但那是另一个话题 |
核心原则:
- 如果方法需要操作某个具体对象的数据,用
self。 - 如果方法主要操作类本身的数据,或者需要根据类进行多态操作,用
cls。