当前位置:首页>python>什么是 Python 面向对象中的继承特点?

什么是 Python 面向对象中的继承特点?

  • 2026-10-11 07:40:47
什么是 Python 面向对象中的继承特点?

下面这段代码,线上排查权限问题时我见过类似的。

classStaff:
defcan_export(self):
returnFalse

classAdmin(Staff):
pass

user = Admin()
print(user.can_export())  # False

一眼看过去,Admin 是管理员,怎么导出权限还是 False?

别急着骂配置中心,也别急着查数据库。这里的问题很简单:Admin 继承了 Staff,但是它自己没重写 can_export(),所以 Python 顺着继承链往父类里找,最后执行的还是 Staff.can_export()。

这就是 Python 面向对象里继承最常见的一个特点:子类会自动拥有父类的属性和方法。

继承不是为了少写几行代码,真只为了少写代码,后面大概率会写出一堆奇怪的父类。继承更像是在表达一种关系:Admin 是一种 Staff,CsvReport 是一种 Report,RetryTask 是一种 Task。

比如报表导出,我一般会这么拆:

classExportJob:
def__init__(self, operator, rows):
        self.operator = operator
        self.rows = rows

defcheck_rows(self):
ifnot self.rows:
raise ValueError("rows is empty")

defrun(self):
        self.check_rows()
return self.build_file()

defbuild_file(self):
raise NotImplementedError("subclass must build file")


classCsvExportJob(ExportJob):
defbuild_file(self):
        lines = []
for row in self.rows:
            lines.append(",".join(str(row.get(k, "")) for k in ("id", "name", "amount")))
return"\n".join(lines)


job = CsvExportJob("dong", [
    {"id": 1001, "name": "order_a", "amount": 32.5},
    {"id": 1002, "name": "order_b", "amount": 19.8},
])

print(job.run())

这里 CsvExportJob 没有写 run(),但它能调用。因为 run() 是父类 ExportJob 给它的。

这个设计里有个细节:父类负责固定流程,子类只负责变化点。check_rows()、run() 这些流程不应该每个导出类都抄一遍,不然后面加一个空数据校验,三个导出类漏改两个,很正常。

但 build_file() 我故意在父类里抛了 NotImplementedError。这个地方我不喜欢写成空实现。空实现太安静,出了问题还要查半天。直接抛异常,谁没实现,启动或者调用时就露出来。

继承的第二个特点,是子类可以重写父类方法。

classStaff:
def__init__(self, name):
        self.name = name

defcan_export(self):
returnFalse


classAdmin(Staff):
defcan_export(self):
returnTrue


print(Admin("root").can_export())  # True

这时候再调用 can_export(),Python 先看 Admin 有没有这个方法,有就直接用子类自己的,不会再去父类拿。

所以排查这类问题时,我一般先看两件事:对象实际类型是什么,方法到底在哪个类上命中的。

user = Admin("root")

print(type(user))
print(user.can_export.__qualname__)

输出类似:

<class '__main__.Admin'>
Admin.can_export

__qualname__ 这个小东西很实用。尤其项目里一堆父类、Mixin、抽象基类叠在一起时,别靠猜,直接看方法来源。

继承还有一个容易被写乱的地方:子类初始化。

很多人写子类时直接把 __init__ 覆盖掉,然后父类里的字段没初始化,后面某个方法一调用就炸。

classImportTask:
def__init__(self, task_id):
        self.task_id = task_id
        self.status = "created"

defmark_running(self):
        self.status = "running"


classUserImportTask(ImportTask):
def__init__(self, task_id, filename):
        super().__init__(task_id)
        self.filename = filename

这里的 super().__init__(task_id) 不能随手省。

因为 UserImportTask 重写了 __init__ 后,父类的 __init__ 不会自动执行。你不主动调,task_id、status 这些父类字段就不存在。

我见过这种报错:

AttributeError: 'UserImportTask' object has no attribute 'status'

看到这种,不要第一反应去查属性有没有拼错。先看子类是不是覆盖了 __init__,又忘了调 super()。

Python 还支持多继承,这个东西能用,但别乱用。

classJsonLogMixin:
deflog_payload(self):
return {
"task_id": self.task_id,
"status": self.status
        }


classImportTask:
def__init__(self, task_id):
        self.task_id = task_id
        self.status = "created"


classProductImportTask(JsonLogMixin, ImportTask):
pass


task = ProductImportTask("imp_202405")
print(task.log_payload())

这里 JsonLogMixin 只提供日志能力,不负责业务主流程。像这种我觉得可以接受。

但如果两个父类都写了同名方法,就要小心了。Python 会按照 MRO,也就是方法解析顺序去找。

classLocalCache:
defload(self):
return"load from local"


classRemoteCache:
defload(self):
return"load from remote"


classConfigCache(LocalCache, RemoteCache):
pass


print(ConfigCache().load())
print(ConfigCache.__mro__)

输出会是:

load from local
(<class '__main__.ConfigCache'>, <class '__main__.LocalCache'>, <class '__main__.RemoteCache'>, <class 'object'>)

LocalCache 写在前面,所以先命中它。

这个顺序不是小事。多继承里如果混了业务父类、工具父类、框架父类,方法名再撞一下,问题会很隐蔽。代码看着没报错,实际执行的却不是你以为的那个方法。

所以我自己的习惯是:业务主线尽量单继承;多继承只拿来做 Mixin,而且 Mixin 不要藏状态,不要偷偷改核心流程。

Python 继承还有一个基础点:所有类最终都继承自 object。平时不用写,但它在背后一直在。

classOrder:
pass

print(Order.__mro__)

输出:

(<class '__main__.Order'>, <class 'object'>)

这也是为什么很多基础方法,比如对象表示、属性访问、比较行为,都能被类重写。

继承用得好,代码会很稳:公共流程放父类,变化点留给子类。继承用得烂,父类会变成垃圾桶,什么字段都往里塞,最后谁也不敢改。

判断一个继承设计靠不靠谱,我通常就看一句话能不能说顺:

CsvExportJob 是一种 ExportJob,顺。

UserService 是一种 BaseService,勉强。

OrderHandler 是一种 CommonUtils,这就不对味了。

继承不是工具箱,父类也不是公共方法收纳盒。它表达的是类型关系。这个边界守住,Python 的继承就不难用。

最新文章

随机文章