当前位置:首页>python>什么是 Python 类中的 self?

什么是 Python 类中的 self?

  • 2026-10-11 06:50:46
什么是 Python 类中的 self?

线上脚本一跑就炸:

TypeError: OrderCleaner.mark_paid() missing 1 required positional argument: 'self'

这种报错我一般先不看业务逻辑,先看方法是怎么调用的。八成不是订单状态错了,也不是数据库字段错了,是你把“类里的方法”当成普通函数直接调了。

看一段很常见的代码:

classOrderCleaner:
defmark_paid(self, order_id):
        print(f"订单 {order_id} 标记为已支付")


OrderCleaner.mark_paid("A1024")

这段代码看着没毛病,实际会报错。

原因就在 self。

mark_paid 写在类里面,并且第一个参数是 self,它就是一个实例方法。实例方法不是直接给类调用的,它要先有一个对象。

应该这样写:

classOrderCleaner:
defmark_paid(self, order_id):
        print(f"订单 {order_id} 标记为已支付")


cleaner = OrderCleaner()
cleaner.mark_paid("A1024")

这里 cleaner.mark_paid("A1024") 表面只传了一个参数,实际 Python 偷偷帮你传了两个:

OrderCleaner.mark_paid(cleaner, "A1024")

第一个参数 cleaner,就落到了 self 上。

所以 self 不是玄学,也不是装饰用的。它就是当前这个对象自己。

我刚开始看 Python 类的时候,也觉得这个写法别扭。Java 里是 this,Python 非要手写一个 self。后来写多了就知道,这东西虽然啰嗦,但它很直白:这个方法到底在操作哪个对象,一眼能看出来。

比如下面这个例子:

classUserLoginStat:
def__init__(self, user_id):
        self.user_id = user_id
        self.fail_count = 0

defrecord_fail(self):
        self.fail_count += 1
        print(f"user={self.user_id}, fail_count={self.fail_count}")


u1 = UserLoginStat("U1001")
u2 = UserLoginStat("U2002")

u1.record_fail()
u1.record_fail()
u2.record_fail()

输出大概是:

user=U1001, fail_count=1
user=U1001, fail_count=2
user=U2002, fail_count=1

这里有个点别看漏了。

u1 和 u2 是两个对象。它们都调用 record_fail,但里面的 self 不一样。

u1.record_fail() 里的 self 指向 u1。

u2.record_fail() 里的 self 指向 u2。

所以 u1.fail_count 加了两次,不会影响 u2.fail_count。

这才是 self 真正干的事:把数据绑到当前对象上。

再看一个容易写坏的地方。

classExportTask:
    file_count = 0

defadd_file(self, filename):
        self.file_count += 1
        print(f"加入文件: {filename}, 当前数量: {self.file_count}")


task_a = ExportTask()
task_b = ExportTask()

task_a.add_file("2026-订单.csv")
task_b.add_file("2026-退款.csv")

这里 file_count 写在类下面,看着像大家共享一个计数。可一旦你写了:

self.file_count += 1

Python 会优先在当前对象上创建一个 file_count。也就是说,task_a 有自己的,task_b 也有自己的。

如果你真想让所有对象共享一个计数,就别用 self 改它,要用类名:

classExportTask:
    total_file_count = 0

defadd_file(self, filename):
        ExportTask.total_file_count += 1
        print(f"加入文件: {filename}, 总文件数: {ExportTask.total_file_count}")

这个坑在线上脚本里挺常见,尤其是写批处理、导入导出、日志统计的时候。看起来只是一个变量放错位置,跑一批数据之后,统计数全乱。

还有人会问,self 能不能换成别的名字?

能。

classPriceChecker:
defcheck(this, sku, price):
        this.sku = sku
        this.price = price

这代码能跑。

但我一般不这么写。不是不能,是没必要。团队里别人看到第一个参数不是 self,第一反应肯定是停一下。代码里最怕这种“能跑但膈应”的写法,尤其排线上问题的时候,别人没空欣赏个性。

还有一种情况,方法里根本没用到对象数据,那就别硬写实例方法。

比如:

classAmountParser:
defparse_cent(self, amount_text):
return int(float(amount_text) * 100)

这个方法没有用 self,说明它不关心当前对象。那我会改成静态方法:

classAmountParser:
    @staticmethod
defparse_cent(amount_text):
return int(float(amount_text) * 100)


cent = AmountParser.parse_cent("19.80")
print(cent)

这样调用更干净,也不会让人误以为它依赖对象状态。

判断一个方法要不要写 self,我一般就看一句话:这个方法需不需要访问当前对象里的数据?

需要,比如用户 ID、连接配置、失败次数、文件路径,那就写 self。

不需要,只是做格式转换、金额换算、字段清洗,那就考虑 staticmethod。

self 不复杂,别背概念。看到它,就把它当成“当前这个对象”。

对象调用方法时,Python 会自动把对象自己塞给 self。

类直接调用实例方法时,你就得自己传对象。

这块想明白了,Python 类里一半看着怪的代码,都顺眼了。

最新文章

随机文章