源本科技 | 码上会

Python 使用 self 作为默认参数

2026/01/16
7
0

学习目标

  • 理解 self 在 Python 类方法中的作用与本质

  • 掌握 self 如何实现对象属性的绑定与访问

  • 明确 Python 为何选择显式传递实例而非隐式处理

  • 能够正确在类中使用 self 定义属性和方法

  • 了解 self 与其它语言(如 Java、C++)中 this 的异同


什么是 self

在 Python 中,self 不是关键字,而是一种约定俗成的命名规范。它是类方法的第一个参数,用于代表当前类的实例对象

当你调用一个对象的方法时,Python 会自动将该对象本身作为第一个参数传入,通常我们将其命名为 self

class Car:
    def __init__(self, brand, model):
        self.brand = brand   # 将品牌存入当前实例
        self.model = model   # 将型号存入当前实例
    
    def display(self):
        return f"{self.brand} {self.model}"

# 创建实例
my_car = Car("丰田", "卡罗拉")

# 调用方法
print(my_car.display())  # 输出:丰田 卡罗拉

实际上,my_car.display() 等价于 Car.display(my_car)。Python 自动将 my_car 作为 self 传入。


为什么需要 self

1. 显式优于隐式

这是 Python 的核心设计哲学之一。通过显式写出 self

  • 代码意图更清晰:一眼看出方法操作的是实例数据

  • 避免魔法行为:不像某些语言自动注入 this,Python 让一切可见

  • 减少歧义:尤其在继承和多态场景下,明确知道操作的是哪个对象

2. 区分实例属性与局部变量

没有 self,Python 无法知道你是在访问对象属性还是定义局部变量。

class Counter:
    def __init__(self):
        self.count = 0  # 实例属性
    
    def increment(self):
        count = count + 1  # 错误!count 是未定义的局部变量
        # 正确写法:self.count = self.count + 1

self.count 明确表示“这个对象的 count 属性”。

3. 支持动态属性与灵活性

Python 允许在运行时为对象添加新属性,self 机制天然支持这种动态性:

class Person:
    def set_name(self, name):
        self.name = name  # 动态创建 name 属性

p = Person()
p.set_name("李明")
print(p.name)  # 李明

工作机制

对象创建与方法调用过程

class Circle:
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius ** 2

c = Circle(5)       # 1. 创建实例
result = c.area()   # 2. 调用方法

内部执行流程:

  1. Circle(5) → 调用 __init__(c, 5),其中 c 是新创建的对象

  2. c.area() → 调用 area(c),自动传入 c 作为 self

  3. 方法内部通过 self.radius 访问 cradius 属性

你可以把 self 理解为“当前正在操作的那个对象”。


关于命名:必须叫 self 吗?

不是! self 只是惯例,你可以使用任何合法标识符:

class Dog:
    def __init__(instance, name):
        instance.name = name
    
    def bark(this_instance):
        print(f"{this_instance.name} 汪汪叫!")

dog = Dog("旺财")
dog.bark()  # 旺财 汪汪叫!

强烈建议始终使用 self,原因如下:

  • 所有 Python 开发者都熟悉这一约定

  • 代码可读性和一致性大幅提升

  • IDE 和工具链针对 self 有特殊支持(如语法高亮、自动补全)


多实例隔离

每个对象拥有独立状态

self 确保不同实例的数据互不干扰:

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance
    
    def deposit(self, amount):
        self.balance += amount

# 创建两个独立账户
account1 = BankAccount("张三", 1000)
account2 = BankAccount("李四", 2000)

account1.deposit(500)

print(account1.balance)  # 1500
print(account2.balance)  # 2000(未受影响)

每次方法调用时,self 指向不同的实例,因此修改的是各自的数据。


与其他语言的对比

语言

实例引用方式

是否显式

Python

self.attribute

Java/C#

this.attribute

可选(通常省略)

C++

this->attribute

可选

JavaScript

this.attribute

是(但在箭头函数中有差异)

Python 的设计选择强调透明性:即使可以隐式处理,也坚持显式写出,以提升代码清晰度。


注意事项

误区 1:self 是关键字

  • 事实:self 只是普通参数名,可更改(但不应更改)

误区 2:静态方法也需要 self

  • 事实:静态方法(用 @staticmethod 装饰)不接收 self

class MathUtils:
    @staticmethod
    def add(a, b):  # 无 self 参数
        return a + b

print(MathUtils.add(3, 5))  # 8

误区 3:类方法使用 self

  • 事实:类方法(用 @classmethod 装饰)接收 cls(代表类本身),而非 self

class Person:
    species = "Homo sapiens"
    
    @classmethod
    def get_species(cls):
        return cls.species  # cls 指向 Person 类

重点总结

  • self 是 Python 类方法的第一个参数,代表当前实例对象

  • 它不是关键字,而是强制性的命名惯例(应始终使用 self

  • Python 自动将实例作为 self 传入方法调用

  • self 使对象属性访问显式化,符合“显式优于隐式”原则

  • 每个实例通过 self 拥有独立的数据空间,互不干扰

  • 静态方法和类方法不使用 self,分别使用无参数和 cls


思考题

  1. 如果 Python 像 Java 一样隐式处理实例引用(即省略 self),会对语言的动态特性(如运行时添加属性)产生什么影响?

  2. 在多重继承场景中,self 如何帮助开发者明确方法调用的目标对象?

  3. 为什么 Python 在方法定义时要求显式写出 self,但在调用时却自动传入?这种设计如何平衡了显式性与易用性?