2025-10-06 Python不基础12——class原理
文章目录
- 1. 表面代码与字节码
- 2. `build_class`
- 2.1. 传入两个关键参数
- 2.2. 执行流程
- 步骤1:初始化元类(`meta`)与命名空间(`namespace`)
- 步骤2:执行类内部的`code object`,填充命名空间
- 步骤3:调用元类(`type`)创建类对象
- 步骤4:返回类对象,赋值给类名变量
- 3. 元类`type`:创建类的“类”
- 3.1. 类的“类型”
- 3.2. `type`创建流程
- 步骤1:`type.__new__`:创建类对象的“骨架”
- 步骤2:`type.__init__`:初始化类对象的“细节”
- 4. 动态创建类
- 5. 流程总结
本文参考视频链接:
- https://www.bilibili.com/video/BV1mB4y1m7FT
当我们在Python中写class
定义类时,底层究竟经历了哪些步骤?
1. 表面代码与字节码
先从一个最简单的类定义示例切入,通过字节码分析,初步发现类定义的“不简单”——类内部代码会被编译为特殊的字节码对象,且依赖内置函数完成最终创建。
# 定义一个简单的类A
class A:name = "AAA" # 类属性def F(self): # 类方法print(1)
通过查看上述代码的字节码,发现两个核心特点:
① 类内部的函数与外部函数无差异
- 类中定义的
F
方法,其字节码与在类外部定义的普通函数(如def F(): print(1)
)完全一致; - 结论:类内部的函数本质仍是普通函数,只是后续会被“挂载”到类上,成为类方法。

② 类内部代码被编译为code object
- 类
A
的内部代码(name = "AAA"
和def F(...)
)会被单独编译成一个code object
(字节码对象),可理解为“一段可执行的代码片段”; - 这个
code object
的作用:后续会被执行,其产生的变量(name
、F
)会成为类的属性/方法。

字节码中出现了load_build_class
指令,该指令的作用是:将Python内置的build_class
函数压入栈中,而build_class
是类创建的“核心协调者”。

2. build_class
build_class
函数是连接“类内部代码”与“最终类对象”的桥梁,其核心工作是处理类内部变量、创建命名空间,并调用元类创建类。
2.1. 传入两个关键参数
当Python执行class A:
时,会自动调用build_class
函数,且传入两个核心参数:
参数类型 | 具体内容 |
---|---|
code object | 类内部代码(name = "AAA" 、def F(...) )编译后的字节码对象 |
类名(字符串) | 即"A" ,用于后续给类对象命名 |
(可选)父类 tuple | 若类有继承(如class A(B): ),则传入父类组成的tuple,默认是空tuple(() ) |

2.2. 执行流程
build_class
函数的执行分4步,最终为类创建“命名空间”,并触发元类的调用:
步骤1:初始化元类(meta
)与命名空间(namespace
)
- 元类(
meta
):在无显式指定元类、无父类的情况下,meta
默认被赋值为type
(Python的内置元类,所有普通类的“类”都是type
);- 补充:“元类”即“创建类的类”,后续会进一步解析
type
的作用。
- 补充:“元类”即“创建类的类”,后续会进一步解析
- 命名空间(
namespace
):创建一个空字典({}
),用于保存类内部代码执行后产生的所有变量(如name
、F
)。

步骤2:执行类内部的code object
,填充命名空间
build_class
会调用exec
(或类似执行逻辑),运行类内部的code object
,并将执行过程中产生的局部变量存入namespace
;- 执行细节:
- 执行
name = "AAA"
:向namespace
中添加键值对{"name": "AAA"}
; - 执行
def F(self): print(1)
:定义函数F
,向namespace
中添加键值对{"F": <function F at 0x...>}
; - 自动添加内置属性:除了显式定义的变量,还会自动向
namespace
添加__module__
(类所在模块,如"__main__"
)和__name__
(类名,如"A"
)。
- 执行
- 执行后
namespace
的结构:{"__module__": "__main__","__name__": "A","name": "AAA","F": <function A.F at 0x...> }

步骤3:调用元类(type
)创建类对象
这是类创建的“重头戏”——build_class
会调用meta
(默认是type
),传入3个参数创建类对象:
- 类名(字符串):
"A"
; - 父类 tuple:默认
()
(无父类时,Python会自动让其继承object
,即最终父类是(object,)
); - 命名空间(字典):即步骤2中填充好的
namespace
。
- 代码层面的等价操作:
type("A", (), namespace)
,这也是“动态创建类”的核心写法(第 3 章详解)。
步骤4:返回类对象,赋值给类名变量
type
调用后返回的结果是一个类对象(即我们平时用的A
);build_class
将这个类对象返回,并赋值给变量A
;- 最终:
A
成为类对象,我们可以通过A.name
访问类属性,A.F
访问类方法,A()
创建实例。
3. 元类type
:创建类的“类”
Python中所有通过class
关键字定义的普通类,其“类”都是type
(即type
是创建类的元类)。这部分将拆解type
如何接收参数并生成类对象。
3.1. 类的“类型”
-
对于实例:
a = A()
,type(a)
返回A
(实例的类型是类); -
对于类:
type(A)
返回type
(类的类型是元类type
);class A:passa = A() print(type(a)) # 输出:<class '__main__.A'>(a的类型是A) print(type(A)) # 输出:<class 'type'>(A的类型是type)
3.2. type
创建流程
当build_class
调用type(类名, 父类, 命名空间)
时,type
会通过两个核心方法完成类创建:type.__new__
和type.__init__
(对应Python中的魔术方法__new__
和__init__
)。
步骤1:type.__new__
:创建类对象的“骨架”

-
作用:分配类对象的内存空间,初始化类的核心属性(如
__name__
、__bases__
、__dict__
); -
关键操作:
- 将传入的“类名”赋值给类的
__name__
属性; - 将传入的“父类tuple”(默认
(object,)
)赋值给类的__bases__
属性(类的父类集合); - 将传入的“命名空间”复制一份,赋值给类的
__dict__
属性(类的属性字典,后续访问A.name
就是从这里查找)。
- 将传入的“类名”赋值给类的
-
返回值:创建好的“空”类对象(骨架已搭好,属性已初始化)。
步骤2:type.__init__
:初始化类对象的“细节”
- 作用:对
type.__new__
创建的类对象进行进一步初始化(如处理类的特殊属性、魔术方法等); - 示例:若类中定义了
__init__
、__str__
等魔术方法,type.__init__
会确保这些方法被正确挂载到类的__dict__
中,供后续实例调用。

经过type
的处理,最终的类对象A
包含以下核心属性:
类属性名 | 内容 |
---|---|
A.__name__ | 类名:"A" |
A.__bases__ | 父类集合:(object,) (默认继承object ) |
A.__dict__ | 类的属性字典:包含name 、F 、__module__ 等(即步骤2的namespace ) |
A.__module__ | 类所在模块:"__main__" (从__dict__ 中继承) |
4. 动态创建类
静态定义类(class A:
)与动态创建类(type(...)
)完全等价,这进一步验证了类创建的底层逻辑——本质是调用type
传入3个参数。
以下代码与前文的class A:
完全等价,且执行结果一致:
# 步骤1:定义类内部的变量(对应class A:内部的代码)
def F(self):print(1)
namespace = {"__module__": "__main__", # 自动添加的内置属性"__name__": "A", # 自动添加的内置属性"name": "AAA", # 类属性"F": F # 类方法
}# 步骤2:调用type创建类对象,传入3个参数:类名、父类、命名空间
A = type("A", (), namespace) # 等价于class A: ...# 验证结果:与静态定义的类完全一致
print(A.name) # 输出:AAA
a = A()
a.F() # 输出:1
print(type(A)) # 输出:<class 'type'>
type
有两种不同的用法,这是Python的“历史包袱”,但本质都与“类型”相关:
用法 | 传入参数数量 | 作用 | 示例 |
---|---|---|---|
查看对象的类型 | 1个 | 返回传入对象的类型(即“实例→类”的映射) | type(A()) → <class '__main__.A'> |
创建新的类对象 | 3个 | 作为元类,创建新的类(即“元类→类”的映射) | type("A", (), {}) → 类对象A |
5. 流程总结
将前面的字节码、build_class
、type
串联起来,类定义的完整流程可分为5步,每一步都有明确的“执行者”和“目标”:
- 编译阶段:类内部代码(属性、方法)被编译为
code object
,等待执行; - 协调阶段:
build_class
接收code object
,创建namespace
并执行代码,收集类的所有变量; - 创建阶段:
type
(元类)接收3个核心参数,生成类对象,最终赋值给类名(如A
)。