在python中什么是 self 和 cls?

在 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。
相关推荐
我的xiaodoujiao20 分钟前
Django 基础知识详细图文教程 12-Django 模型定义与使用 2
后端·python·django
孙启超1 小时前
【AI开发之Rust】第 22 课:一键多平台与工程收尾 —— CI、发布检查与结课
开发语言·后端·rust
happylifetree8 小时前
Python09:核心语法-数据存储与运算-字面量
python
落魄实习生8 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
前端snow8 小时前
ai agent --- postgreSQL 关系型数据库
前端
心之语歌9 小时前
Tkinter 画布基本梳理
运维·服务器·python
茉莉玫瑰花茶9 小时前
GO [ 方法 ]
开发语言·后端·golang
Wx-bishekaifayuan9 小时前
django个性化旅游路线推荐平台49005-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
卷无止境9 小时前
ECharts:把数据变成故事的可视化利器
前端
Eric_见嘉9 小时前
在职前端 Skill 和 MCP 分享
前端·后端·agent