Python基础 - 类中__init_subclass_方法执行顺序

简介: 本文详解Python类与实例化时的执行顺序:类定义时先执行类体,再调用`__init_subclass__`;实例化时先调用`__new__`创建对象,再调用`__init__`初始化。`__init_subclass__`仅在类定义时执行一次,确保父类可访问子类属性,如SQLAlchemy中表结构的构建原理。

__init_subclass__是在子类定义时,类体执行完成后立即被Python自动调用的初始化钩子方法。


下面定义一个Python的脚本去分析类与实例化类的执行顺序

"""
详细演示类定义和实例化的执行顺序
"""

from typing import Any


print("阶段 1: 定义基类")


class Base:
    def __init_subclass__(cls, **kwargs: Any) -> None:
        print(f"  [Base.__init_subclass__] 被调用!子类: {cls.__name__}")
        print(f"  [Base.__init_subclass__] 此时类体已经执行完成!")
        print(f"  [Base.__init_subclass__] cls.__dict__ = {list(cls.__dict__.keys())}")
        super().__init_subclass__(**kwargs)

    def __new__(cls):
        print(f"  [Base.__new__] 被调用!类: {cls.__name__}")
        print(f"  [Base.__new__] 这是在实例化时调用的!")
        return super().__new__(cls,)

    def __init__(self,):
        print(f"  [Base.__init__] 被调用!实例: {self.__class__.__name__}")
        print(f"  [Base.__init__] 这是在实例化时调用的!")
        super().__init__()
    def Test(self):
        print("Test ...")


print("阶段 2: 定义子类")

print("执行: class Child(Base):")

class Child(Base):
    print("  [类体执行] 第 1 行:print 语句")
    name = "Child"
    print("  [类体执行] 第 2 行:定义 name 属性")
    age = 18
    print("  [类体执行] 第 3 行:定义 age 属性")
    print("  [类体执行] 类体执行完成!")
    # 注意:__init_subclass__ 会在类体执行完成后被调用


print("阶段 3: 实例化")

print("执行: child = Child()")

child = Child()
print(f"\n创建的对象: {child}")
print(f"child.name = {child.name}")
print(f"child.age = {child.age}")


print("总结")

print("1. 类体中的代码先执行(从上到下)")
print("2. 类体执行完成后,Python 自动调用 __init_subclass__")
print("3. 实例化时,先调用 __new__,再调用 __init__")
print("4. 实例化时不会调用 __init_subclass__(因为类已经定义好了)")

执行结果:

阶段 1: 定义基类
阶段 2: 定义子类
执行: class Child(Base):
  [类体执行] 第 1 行:print 语句
  [类体执行] 第 2 行:定义 name 属性
  [类体执行] 第 3 行:定义 age 属性
  [类体执行] 类体执行完成!
  [Base.__init_subclass__] 被调用!子类: Child
  [Base.__init_subclass__] 此时类体已经执行完成!
  [Base.__init_subclass__] cls.__dict__ = ['__module__', 'name', 'age', '__doc__']
阶段 3: 实例化
执行: child = Child()
  [Base.__new__] 被调用!类: Child
  [Base.__new__] 这是在实例化时调用的!
  [Base.__init__] 被调用!实例: Child
  [Base.__init__] 这是在实例化时调用的!

创建的对象: <__main__.Child object at 0x00000219B9258560>
阶段 3: 实例化
执行: child = Child()
  [Base.__new__] 被调用!类: Child
  [Base.__new__] 这是在实例化时调用的!
  [Base.__init__] 被调用!实例: Child
  [Base.__init__] 这是在实例化时调用的!

创建的对象: <__main__.Child object at 0x00000219B9258560>
child.name = Child
child.age = 18
总结
1. 类体中的代码先执行(从上到下)
2. 类体执行完成后,Python 自动调用 __init_subclass__
3. 实例化时,先调用 __new__,再调用 __init__
4. 实例化时不会调用 __init_subclass__(因为类已经定义好了)
(dify-api) PS D:\AI\DIFY\dify-main>

执行: child = Child()
  [Base.__new__] 被调用!类: Child
  [Base.__new__] 这是在实例化时调用的!
  [Base.__init__] 被调用!实例: Child
  [Base.__init__] 这是在实例化时调用的!

创建的对象: <__main__.Child object at 0x00000219B9258560>
  [Base.__new__] 这是在实例化时调用的!
  [Base.__init__] 被调用!实例: Child
  [Base.__init__] 这是在实例化时调用的!

创建的对象: <__main__.Child object at 0x00000219B9258560>
  [Base.__init__] 被调用!实例: Child
  [Base.__init__] 这是在实例化时调用的!

创建的对象: <__main__.Child object at 0x00000219B9258560>
  [Base.__init__] 这是在实例化时调用的!

创建的对象: <__main__.Child object at 0x00000219B9258560>

创建的对象: <__main__.Child object at 0x00000219B9258560>
child.name = Child
child.age = 18
总结
1. 类体中的代码先执行(从上到下)
2. 类体执行完成后,Python 自动调用 __init_subclass__
3. 实例化时,先调用 __new__,再调用 __init__
4. 实例化时不会调用 __init_subclass__(因为类已经定义好了)

执行顺序详解

1. 类定义时的执行顺序

============================================================
完整执行流程总结
============================================================
阶段 1: 定义 Base 类时
  1. 执行 Base 类体(属性定义、方法定义等)
  2. 定义 __init_subclass__ 方法(只是定义,不执行)
  3. Base 类定义完成

阶段 2: 定义 Child 类时
  1. 执行 Child 类体(按顺序执行所有代码)
  2. Child 类体执行完成
  3. Python 自动调用 Base.__init_subclass__(Child)
  4. Child 类定义完成

阶段 3: 实例化 Child 时
  1. 调用 Child.__new__(创建对象)
  2. 调用 Child.__init__(初始化对象)

注意:实例化时不会调用 __init_subclass__,因为类已经定义好了

2. 为什么类体先执行,然后才调用父类 __init_subclass__

这是 Python 的设计:

  1. 类体先执行:Python 需要先执行类体,收集所有类属性
  2. 然后调用 __init_subclass__:此时类属性已经收集完成,父类可以访问这些属

查看输出:

[类体执行] 第 1 行:print 语句
[类体执行] 第 2 行:定义 name 属性
[类体执行] 第 3 行:定义 age 属性
[类体执行] 类体执行完成!
[Base.__init_subclass__] 被调用!子类: Child
[Base.__init_subclass__] 此时类体已经执行完成!
[Base.__init_subclass__] cls.__dict__ = ['__module__', 'name', 'age', '__doc__']

可以看到__init_subclass__ 被调用时,nameage 已经在 cls.__dict__ 中了。

3. 为什么实例化时不调用 __init_subclass__

因为 __init_subclass__ 只在类定义时调用一次,不是每次实例化都调用。

实例化时的调用链:
1. Child() 被调用
2. Python 调用 Child.__new__(Child)  ← 创建对象
3. Python 调用 Child.__init__(self)  ← 初始化对象
4. 对象创建完成

__init_subclass__ 不会在这里调用,因为:

  • 类已经定义好了(在导入时)
  • __init_subclass__ 只在类定义时调用一次
  • 实例化时只需要创建对象,不需要重新定义类


4. 关键点总结

问题 答案
为什么类体先执行? Python 需要先收集所有类属性,然后才能让 __init_subclass__ 访问它们
为什么 __init_subclass__ 在类体之后? 这样父类可以访问子类的所有属性(如 __tablename__mapped_column
为什么实例化时不调用 __init_subclass__ __init_subclass__ 只在类定义时调用一次,实例化时类已经定义好了
实例化时调用什么? __new__(创建对象)和 __init__(初始化对象)

5. 为什么这样设计

  1. 类体先执行:让所有类属性先定义好
  2. 然后调用 __init_subclass__:父类可以访问子类的所有属性
  3. 实例化时只调用 __new____init__:类已经定义好,只需要创建对象

这就是为什么在 SQLAlchemy 中,DeclarativeBase.__init_subclass__ 可以访问 Account.__tablename__ 和所有 mapped_column 定义的原因。这个执行顺序是 Python 的设计,不是 SQLAlchemy 的特殊处理。


6.常见特殊方法的执行时机

__init_subclass__  # 定义子类时自动调用

__new__            # 创建对象时自动调用

__init__           # 初始化对象时自动调用

__str__            # 调用 str(obj) 时自动调用

__repr__           # 调用 repr(obj) 时自动调用

__getattr__        # 访问不存在的属性时自动调用

# ... 还有很多

目录
相关文章
|
14天前
|
存储 运维 安全
阿里云目前活动内云服务器可以买3年吗?可选实例规格、配置及价格参考
在目前阿里云的活动中,经济型e实例支持3年购买,配置涵盖2核4G、4核8G等,例如2核4G 3年1499.40元起,4核8G 3年3249.00元起。采用Intel Xeon Platinum处理器,支持多种处理器内存配比,搭载ESSD Entry云盘,适配中小型网站、开发测试、轻量级应用等场景。
|
21天前
|
存储 自然语言处理 数据库
RAG(检索增强生成)技术简介
RAG(检索增强生成)通过结合信息检索与文本生成,提升大模型回答的准确性与时效性。它利用向量数据库实现语义检索,支持智能客服、医疗诊断、法律咨询等场景,解决知识更新难、专业性不足等问题,并以“按需取用”机制突破上下文限制,兼顾效率与隐私。
281 4
|
6天前
|
关系型数据库 项目管理 数据安全/隐私保护
Leantime:开源项目管理神器
Leantime是一款专为非专业项目经理设计的开源项目管理工具,在Jira的臃肿和Trello的简化之间找到了完美平衡。它集成了战略规划、敏捷看板、甘特图、知识管理、工时跟踪等全面功能,支持Docker一键部署。无论是创业团队还是企业部门,Leantime都能以极低的学习成本,让每位成员轻松参与项目协作。告别过度复杂的工具,用这款轻量而强大的神器,为你的2026年项目计划保驾护航。
85 16
 Leantime:开源项目管理神器
|
7天前
|
机器学习/深度学习 人工智能 计算机视觉
YOLO26改进 - 注意力机制 | 多扩张通道细化器MDCR 通过通道划分与异构扩张卷积提升小目标定位能力
本文介绍了一种在YOLO26目标检测模型中引入高效解码器模块EMCAD的创新方法,以提升模型在资源受限场景下的性能与效率。EMCAD由多个模块构成,其中核心的EUCB(高效上卷积块)通过上采样、深度可分离卷积、激活归一化和通道调整等操作,兼顾了特征质量与计算成本。实验结果显示,该模块在显著减少参数与FLOPs的同时仍具备优异性能。文章还提供了完整的YOLO26模型集成流程、配置和训练实战。
YOLO26改进 - 注意力机制 | 多扩张通道细化器MDCR 通过通道划分与异构扩张卷积提升小目标定位能力
|
7天前
|
机器学习/深度学习 计算机视觉 网络架构
YOLO26改进 - 注意力机制 |融合HCF-Net维度感知选择性整合模块DASI 增强小目标显著性
本文介绍将HCF-Net中的维度感知选择性融合(DASI)模块集成至YOLO26检测头,通过通道分区与Sigmoid自适应加权,融合高/低维及当前层特征,显著提升红外小目标检测精度,在SIRST数据集上超越主流方法。(239字)
|
8天前
|
人工智能 前端开发 测试技术
Violit: Streamlit杀手,无需全局刷新,构建AI快捷面板
Violit 是新一代 Python Web 框架,融合 Streamlit 的简洁语法与 React 的响应式性能。首创 O(1) 信号状态架构,零重运行、无需 `@cache`/`key`/回调,支持桌面原生应用与 30+ 主题,开箱即用、极速如光。
106 15
|
12天前
|
云安全 安全 Cloud Native
阿里云智能云原生应用保护平台CNAPP(原安全中心)详解:费用价格、功能优势及问题解答FAQ
阿里云全新升级智能云原生应用保护平台(CNAPP),融合CWPP、CSPM、CIEM、CTDR四大能力,提供覆盖“事前-事中-事后”的全链路安全防护。支持多云纳管、自动威胁响应与合规检查,助力企业实现安全左移、风险可视、响应自动化。
|
7天前
|
人工智能 开发框架 IDE
AI 时代的量化革命:10分钟开发你的第一个交易策略
本文手把手教你用AI工具10分钟开发首个量化交易策略:从克隆SDK、启动AI IDE,到生成KDJ_RSI组合策略、配置运行环境并实盘验证。零基础也能快速上手,开启AI驱动的量化投资新范式!
142 17
|
6天前
|
存储 缓存 监控
pandas 3.0 内存调试指南:学会区分真假内存泄漏
本文揭秘pandas“内存不释放”的常见误解:非泄漏,实为CoW共享、Arrow缓冲池、视图隐式引用及分配器延迟归还OS内存所致。RSS≠真实占用,排查需结合tracemalloc、objgraph与原生指标,核心是管控引用生命周期。
129 12
pandas 3.0 内存调试指南:学会区分真假内存泄漏
|
5天前
|
机器学习/深度学习 数据采集 人工智能
大模型应用:大模型参数调优:结合本地模型对比多种组合探索差异.7
本文系统解析大模型核心生成参数(如temperature、top_p、top_k、repetition_penalty等)的原理、作用机制与实践影响,结合Qwen1.5-1.8B本地模型实测,通过创意写作、技术问答、代码生成三类任务对比分析参数组合效果,并提供分场景调优建议与黄金配置方案,助力从“调参新手”进阶为“生成质量掌控者”。
82 21

热门文章

最新文章