page contents

Python 的类和对象有什么区别?

比如导订单,一个客户的折扣规则,跑着跑着被另一个客户用了。你第一眼去查数据库,查接口,查缓存,最后发现问题不在外面,就在 Python 类和对象这点事上。

attachments-2026-06-ghlKUAVE6a30a799ee9e5.png脚本里最烦的一种 bug,是看日志每一行都正常,最后汇总数据却串了。

比如导订单,一个客户的折扣规则,跑着跑着被另一个客户用了。你第一眼去查数据库,查接口,查缓存,最后发现问题不在外面,就在 Python 类和对象这点事上。

很多人学 Python 时,会把“类”和“对象”混着叫。平时写小脚本没事,一到稍微有状态的代码,就开始埋坑。

先看一段我见过好几次的写法:

class PriceRule:
    rate = 1.0
    reason = "default"

    def apply(self, amount):
        return amount * self.rate


vip_rule = PriceRule()
normal_rule = PriceRule()

vip_rule.rate = 0.8
vip_rule.reason = "vip discount"

print(vip_rule.apply(100))      # 80.0
print(normal_rule.apply(100))   # 100.0

这段看着没毛病。

PriceRule 是类,vip_rule、normal_rule 是对象。

类像一张加工单,规定这个东西有哪些字段、有哪些动作。对象是按这张单子真正做出来的一份东西。

别把它想成什么高深概念。

PriceRule 本身不会去给某个订单打折,真正干活的是 vip_rule.apply(100)。类负责描述规则,对象负责带着具体数据去执行。

问题是,Python 里类不只是“模板”,它自己也能放数据。这个地方我一般会多看一眼,因为很多事故就是从这里漏出来的。

看下面这个:

class ImportBatch:
    failed_rows = []

    def mark_failed(self, row_no, msg):
        self.failed_rows.append({
            "row_no": row_no,
            "msg": msg
        })


batch_202401 = ImportBatch()
batch_202402 = ImportBatch()

batch_202401.mark_failed(18, "phone empty")
batch_202402.mark_failed(7, "amount invalid")

print(batch_202401.failed_rows)
print(batch_202402.failed_rows)

输出大概率会让新手愣一下:

[
    {'row_no': 18, 'msg': 'phone empty'},
    {'row_no': 7, 'msg': 'amount invalid'}
]
[
    {'row_no': 18, 'msg': 'phone empty'},
    {'row_no': 7, 'msg': 'amount invalid'}
]

两个批次,失败记录混在一起了。

这地方我第一眼就不太信 failed_rows = [] 这种写法。因为它定义在类上,不是定义在对象上。

类变量,所有对象共享。

你以为每个批次一份失败列表,实际上大家共用一个列表。线上导入任务一并发,日志里看着是 A 批次失败,页面上却出现在 B 批次里,排查起来很恶心。

这种状态,老老实实放到对象里:

class ImportBatch:
    def __init__(self, batch_no):
        self.batch_no = batch_no
        self.failed_rows = []

    def mark_failed(self, row_no, msg):
        self.failed_rows.append({
            "batch_no": self.batch_no,
            "row_no": row_no,
            "msg": msg
        })


batch_202401 = ImportBatch("202401")
batch_202402 = ImportBatch("202402")

batch_202401.mark_failed(18, "phone empty")
batch_202402.mark_failed(7, "amount invalid")

print(batch_202401.failed_rows)
print(batch_202402.failed_rows)

这才是正常味道。

__init__ 每创建一个对象就执行一次。你在里面写的 self.failed_rows = [],就是给当前这个对象单独挂一份列表。

这里的 self 也别玄学理解。

谁调用方法,self 就是谁。

batch_202401.mark_failed(18, "phone empty")

这行代码里,self 就是 batch_202401。

换成下面这种写法,其实更直白:

ImportBatch.mark_failed(batch_202401, 18, "phone empty")

只是平时没人这么写。

所以类和对象的区别,可以从这几个动作里看:

class SmsTask:
    provider = "aliyun"

    def __init__(self, mobile, content):
        self.mobile = mobile
        self.content = content
        self.status = "created"

    def send(self):
        print(f"send to {self.mobile}: {self.content}")
        self.status = "sent"


task = SmsTask("13800000000", "your code is 9527")
task.send()

print(SmsTask.provider)
print(task.mobile)
print(task.status)

SmsTask 是类。

task 是对象。

provider 放在类上,表示这一类短信任务默认都走同一个服务商。这个值适合共享。

mobile、content、status 放在对象上,因为每条短信都不一样。

我平时判断一个字段该放类上还是对象上,就问一句:这个东西是不是每个实例都可能不同?

可能不同,放 self 上。

比如手机号、订单号、导入批次号、处理状态、失败原因。

如果确实是全局配置、固定枚举、默认值,可以放类上。但就算能放,我也会克制一点。尤其是列表、字典、集合这种可变对象,没把握就别放类上。

再看一个稍微真实点的写法,订单风控里经常有这种小对象:

class RiskContext:
    source = "order_submit"

    def __init__(self, order_id, user_id, amount):
        self.order_id = order_id
        self.user_id = user_id
        self.amount = amount
        self.tags = []

    def hit(self, tag):
        if tag not in self.tags:
            self.tags.append(tag)

    def need_review(self):
        return self.amount > 5000 or "new_device" in self.tags


ctx = RiskContext("O20260109001", 7731, 6800)
ctx.hit("new_device")

print(ctx.need_review())
print(ctx.tags)

这里类做了三件事:规定字段,封装动作,给对象一套行为。

对象做的事更具体:保存这一笔订单的数据,记录这一笔订单命中了哪些标签,判断这一笔订单要不要人工审核。

类偏“规则”。

对象偏“现场”。

再土一点说,类是工厂里的图纸,对象是流水线上那台具体机器。图纸改了,后面造出来的机器会受影响;已经造出来的机器,自己身上的状态该怎么变,还得看它自己。

写 Python 的类,真正要避开的不是概念背不熟,而是把共享状态和实例状态写混。

我一般看到这种代码会直接改:

class Job:
    logs = []

尤其是 logs、errors、items、cache、result 这种名字,基本都危险。

更稳的写法是:

class Job:
    def __init__(self, job_id):
        self.job_id = job_id
        self.logs = []

    def add_log(self, line):
        self.logs.append(f"{self.job_id} | {line}")

类和对象的区别,最后落到代码里就是一句话:

类负责定义这一类东西长什么样、能干什么;对象是某一次真正创建出来的实例,它带着自己的数据跑。

写业务代码时别只盯着语法能不能跑。能跑不代表状态没串。Python 这块很宽松,宽松的地方,往往也最容易把坑藏进去。

更多相关技术内容咨询欢迎前往并持续关注好学星城论坛了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

 

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

2135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 2228 文章
  3. Pack 2135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章