在python中什么是 self 和 cls?

在 Python 中,selfcls 分别是实例方法类方法的第一个参数。它们虽然名字不同,但都是一种约定俗成的命名,用来接收调用时自动传入的对象。下面详细讲解它们的异同、优劣以及使用场景。


1. 什么是 selfcls

  • 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_attrself.class_attr(若实例没有同名属性)访问类属性;类方法直接通过 cls.class_attr 访问。
  • 都可以被继承 :子类会继承父类的实例方法和类方法,且 self / cls 会根据实际调用者改变(多态)。

3. 不同点

对比项 实例方法(self) 类方法(cls)
装饰器 不需要 需要 @classmethod
第一个参数代表什么 实例对象 类对象
能否访问实例属性 ✅ 可以(self.attr ❌ 不可以(没有 selfcls 是类,无法知道具体实例)
能否修改实例状态 ✅ 可以 ❌ 不可以
能否访问/修改类属性 ✅ 可以(通过 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_jsonfrom_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
相关推荐
我会冲击波1 小时前
vibe coding 一个多月,我把 Claude Code + Pi 做成了桌面 IDE,还给它移植了 VS Code 的编辑体验
前端·javascript·后端
kyson_1 小时前
前端性能优化全链路实践指南:从网络、缓存到渲染与监测
前端
nanawinona1 小时前
2026年手工思路量化后,工具重点会怎样变化
人工智能·python
前端fighter1 小时前
线上崩溃?使用 TRAE Work 5分钟把混淆日志翻译成人话并输出复盘报告
前端·vue.js·程序员
网安蟹佬霸1 小时前
CTF Web方向解题全攻略:从信息收集到getshell(附实战payload与脚本)
android·前端·网络·安全·web安全·网络安全·网安
杨充1 小时前
05.多用组合和少继承
开发语言·bash
前端玩坑坑1 小时前
CSS Container Queries 容器查询入门教程
前端
带多刺的玫瑰1 小时前
Leecode#4刷题之寻找两个正序数组的中位数
java·前端·算法