You need to enable JavaScript to run this app. 为何type.__new__会调用__init_subclass__?Django类创建时序疑问
优惠活动
大模型
产品
解决方案
定价
更多

为何type.__new__会调用__init_subclass__?Django类创建时序疑问

类创建流程中__init_subclass__与元类的时序疑问

实际遇到的问题

  • 尝试将__init_subclass__应用到Django模型类时,无法达到预期效果
  • 观察到父类的__init_subclass__执行时,元类尚未完成子类的全部创建工作
  • 已知该问题可以通过自定义元类规避,但始终不理解这种时序设计的原因
  • 此前的固有认知是:所有类似__new__的类构造操作,应当在所有类似__init__的类初始化操作之前全部完成,但实际观察到的执行时序和这个认知存在明显偏差

复现实验

测试代码

class MetaMeta(type):
    print('parsing MetaMeta')
    def __call__(cls, *args, **kwargs):
        print('entering MetaMeta.__call__')
        instance = super().__call__(*args, **kwargs)
        print('leaving MetaMeta.__call__')
        return instance

class Meta(type, metaclass=MetaMeta):
    print('parsing Meta')
    def __init__(self, *args, **kwargs):
        print('  entering Meta.__init__')
        super().__init__(*args, **kwargs)
        print('  leaving Meta.__init__')
    def __new__(cls, *args, **kwargs):
        print(f'  entering Meta.__new__')
        instance = super().__new__(cls, *args, **kwargs)
        print('  leaving Meta.__new__')
        return instance

class Parent(object, metaclass=Meta):
    print('parsing Parent')
    def __init_subclass__(cls, *args, **kwargs):
        print('    entering Parent.__init_subclass__')
        super().__init_subclass__(*args, **kwargs)
        print('    leaving Parent.__init_subclass__')

class Child(Parent):
    print('parsing Child')

运行输出结果

parsing MetaMeta
parsing Meta
parsing Parent
entering MetaMeta.__call__
  entering Meta.__new__
  leaving Meta.__new__
  entering Meta.__init__
  leaving Meta.__init__
leaving MetaMeta.__call__
parsing Child
entering MetaMeta.__call__
  entering Meta.__new__
    entering Parent.__init_subclass__
    leaving Parent.__init_subclass__
  leaving Meta.__new__
  entering Meta.__init__
  leaving Meta.__init__
leaving MetaMeta.__call__

时序观察与核心疑问

从输出可以看到明显的时序偏差:Parent.__init_subclass__的执行是嵌套在Meta.__new__的流程内部完成的,也就是说这个方法触发时,Meta.__new__还未走完整个执行流程,后续的Meta.__init__也尚未开始运行。
围绕这个偏差,核心的疑问包括:

  • 为什么Python(至少在Python 3.9版本的实现中)选择让Meta.__new__触发父类的__init_subclass__,而不是由外层的MetaMeta.__call__在__new__完全执行结束后再调用该方法?这种设计的原因是什么?
  • 有没有办法在不编写自定义元类的前提下,在Parent类中实现一段逻辑,使其执行时机完全卡在Meta.__new__全部执行完成后、Meta.__init__开始执行前?
  • 自己此前对Python类创建流程的理解是否存在根本性偏差?

已参考的关联主题

在梳理问题的过程中,已查阅以下相关资料,但均未找到针对当前时序设计原因的明确解答:

  • Python3元类的调用顺序
  • 元类的__new__与__init__方法参数
  • Python官方文档中关于类创建自定义流程的说明
  • 公开技术渠道关于该时序问题的历史工单与开发者邮件组讨论

内容的提问来源于stack exchange,提问作者Nonanon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.01 04:36:02