Python技术迷

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

user.name 打印出来是“张三”,User.name 却不一定有东西。

这个问题别急着背概念,类和对象的区别,我一般不从“类是模板,对象是实例”开始讲。那句话没错,但太滑了,滑到最后还是不会用。

先看一段现场里更像会出现的代码:

classDevice:
    category = "scanner"

def__init__(self, sn, owner):
        self.sn = sn
        self.owner = owner
        self.online = False

defmark_online(self):
        self.online = True

defshow(self):
returnf"{self.sn} / {self.owner} / {self.online}"

d1 = Device("A1001", "factory-01")
d2 = Device("A1002", "factory-02")

d1.mark_online()

print(d1.show())
print(d2.show())
print(Device.category)

这段代码里,Device 是类,d1、d2 是对象。

但这里真正要盯住的不是这句话,而是:d1 和 d2 各自有自己的 sn、owner、online。d1.mark_online() 改的是 d1 自己,不会把 d2 顺手也改了。

这就是对象最有用的地方:它带着自己的状态。

类更像一套规则。它规定一个设备对象创建出来应该有哪些字段,能执行哪些动作。对象才是真正在内存里干活的那个东西。

我刚学 Python 那会儿,最容易混的是这一句:

d1 = Device("A1001", "factory-01")

很多人以为这是“调用了类”。更准确一点说,是用 Device 这个类创建了一个对象,然后把这个对象交给变量 d1 引用。

可以用 id() 看一下:

d1 = Device("A1001", "factory-01")
d2 = Device("A1001", "factory-01")

print(id(d1))
print(id(d2))
print(d1 is d2)

哪怕两个对象里面的数据一样,它俩也不是一个东西。线上排问题时这点很重要,尤其是你把对象塞进缓存、列表、队列的时候,别看到字段一样就以为是同一个对象。

再看一个坑,类变量和对象变量。

classJob:
    retry_limit = 3

def__init__(self, job_id):
        self.job_id = job_id
        self.retry_count = 0

j1 = Job("sync-user")
j2 = Job("sync-order")

j1.retry_count += 1

print(j1.retry_count)      # 1
print(j2.retry_count)      # 0
print(Job.retry_limit)     # 3

retry_limit 写在类下面,不在 __init__ 里,它是类变量。所有对象默认都能读到它。

retry_count 写在 self 上,它是对象自己的变量。

这地方我一般会多看一眼,因为有些 bug 就是把本该属于对象的数据写成了类变量。

比如下面这个就不太对劲:

classImportTask:
    failed_rows = []

def__init__(self, filename):
        self.filename = filename

defadd_error(self, row_no):
        self.failed_rows.append(row_no)

t1 = ImportTask("a.xlsx")
t2 = ImportTask("b.xlsx")

t1.add_error(3)

print(t2.failed_rows)

你以为 t2.failed_rows 是空的,结果里面有 3。

这不是 Python 抽风,是你把列表放到了类上。failed_rows 成了所有对象共享的一份数据。批量导入、任务状态、错误明细这种东西,基本都不该这么写。

改成这样才像正常业务代码:

classImportTask:
def__init__(self, filename):
        self.filename = filename
        self.failed_rows = []

defadd_error(self, row_no, reason):
        self.failed_rows.append({
"row": row_no,
"reason": reason
        })

task = ImportTask("user_2026_06.xlsx")
task.add_error(17, "mobile is empty")

print(task.failed_rows)

这里的 failed_rows 是每个对象自己的。你起十个导入任务,它们不会互相串数据。

再说 self。

self 不是关键字,但最好别改名字。它代表当前这个对象。

classAccount:
def__init__(self, account_id, balance):
        self.account_id = account_id
        self.balance = balance

deffreeze(self):
        print(f"freeze account: {self.account_id}")

当你写:

acc = Account("U7788", 200)
acc.freeze()

Python 会把 acc 自动传给 freeze 里的 self。所以 freeze 才知道自己冻结的是哪个账户。

这就是类方法看起来少传一个参数的原因。不是没有传,是 Python 帮你传了。

类和对象的区别,落到写代码上,其实就这几个判断:

类负责定义结构和行为。 对象负责保存具体数据。 类只有一份,对象可以有很多个。 写在 self 上的,通常是对象自己的。 直接写在类上的,要小心是不是会被所有对象共享。

我平时看一段 Python 面向对象代码,会先看 __init__。因为对象的关键状态基本都在那里。再看方法里改了哪些 self.xxx。如果一个方法疯狂改类变量,我就会不太放心,尤其是 Web 服务、异步任务、批处理脚本这种长时间运行的程序。

最后再压一句。

类不是为了显得高级,对象也不是为了把简单代码写复杂。它们真正有用的地方,是把“数据”和“操作这份数据的动作”放在一起。订单有订单的状态,设备有设备的状态,任务有任务的失败记录。

代码跑久了,你会发现,能不能分清类和对象,不是考试题,是排 bug 时少绕几圈。