当前位置: 首页 > news >正文

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))完全一致;
  • 结论:类内部的函数本质仍是普通函数,只是后续会被“挂载”到类上,成为类方法。
image-20251006142028609

② 类内部代码被编译为code object

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

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

image-20251006142431856

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(()
image-20251006142702102

2.2. 执行流程

build_class函数的执行分4步,最终为类创建“命名空间”,并触发元类的调用:

步骤1:初始化元类(meta)与命名空间(namespace

  • 元类(meta:在无显式指定元类、无父类的情况下,meta默认被赋值为type(Python的内置元类,所有普通类的“类”都是type);
    • 补充:“元类”即“创建类的类”,后续会进一步解析type的作用。
  • 命名空间(namespace:创建一个空字典({}),用于保存类内部代码执行后产生的所有变量(如nameF)。
image-20251006142839287

步骤2:执行类内部的code object,填充命名空间

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

步骤3:调用元类(type)创建类对象

这是类创建的“重头戏”——build_class会调用meta(默认是type),传入3个参数创建类对象:

  1. 类名(字符串):"A"
  2. 父类 tuple:默认()(无父类时,Python会自动让其继承object,即最终父类是(object,));
  3. 命名空间(字典):即步骤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__:创建类对象的“骨架”

image-20251006143804915
  • 作用:分配类对象的内存空间,初始化类的核心属性(如__name____bases____dict__);

  • 关键操作:

    1. 将传入的“类名”赋值给类的__name__属性;
    2. 将传入的“父类tuple”(默认(object,))赋值给类的__bases__属性(类的父类集合);
    3. 将传入的“命名空间”复制一份,赋值给类的__dict__属性(类的属性字典,后续访问A.name就是从这里查找)。
    image-20251006143933233
  • 返回值:创建好的“空”类对象(骨架已搭好,属性已初始化)。

步骤2:type.__init__:初始化类对象的“细节”

  • 作用:对type.__new__创建的类对象进行进一步初始化(如处理类的特殊属性、魔术方法等);
  • 示例:若类中定义了__init____str__等魔术方法,type.__init__会确保这些方法被正确挂载到类的__dict__中,供后续实例调用。
image-20251006143824339

经过type的处理,最终的类对象A包含以下核心属性:

类属性名内容
A.__name__类名:"A"
A.__bases__父类集合:(object,)(默认继承object
A.__dict__类的属性字典:包含nameF__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_classtype串联起来,类定义的完整流程可分为5步,每一步都有明确的“执行者”和“目标”:

编写class A: ...
Python编译器将类内部代码编译为code object
调用内置函数build_class,传入code object和类名A
build_class创建空namespace,执行code object,将变量存入namespace
build_class调用元类type,传入类名A、父类()、namespace
type创建类对象,赋值给变量A,最终A成为类对象
  1. 编译阶段:类内部代码(属性、方法)被编译为code object,等待执行;
  2. 协调阶段build_class接收code object,创建namespace并执行代码,收集类的所有变量;
  3. 创建阶段type(元类)接收3个核心参数,生成类对象,最终赋值给类名(如A)。
http://www.dtcms.com/a/450126.html

相关文章:

  • 龙泉驿建设局网站谷歌seo是什么职业
  • 从东方仙盟筑基期看 JavaScript 动态生成图片技术-东方仙盟
  • 怎么做电脑网站后台谷歌seo推广服务
  • 【笔记】2.1.1.1 电化学定义与组件特征
  • ISO 11452系列子标准介绍 道路车辆窄带辐射电磁能电干扰的部件试验方
  • 南宁网站制作工具山东建设厅执业资格注册中心网站
  • WebStorm对个人免费开放
  • 免费1级做爰片在线观看网站wordpress QQ登录注册
  • Git仓库Python文件Pylint静态分析
  • 青岛网站建设网站设计游乐网站设计
  • 盘锦网站开发推荐几个做网站比较好的公司
  • 10.6 作业
  • Dnsmasq 详细介绍与应用指南
  • MinIO 控制台功能减少使用mc操作
  • 【多线程-进阶】常⻅的锁策略
  • 通过类比理解TCP\IP五层协议
  • R脚本--PCA分析系列1_v1.0
  • 大模型面试题剖析:深入解析 Transformer 与 MoE 架构
  • wordpress首页没有显示文章图片绵阳网站建设优化
  • VR大空间资料 04 —— VRAF使用体验和源码分析
  • LabVIEW定时循环中止功能
  • 南昌中企动力做的网站怎么样宁波妇科
  • Async++ 源码分析10--ref_count.h
  • 单页面竞价网站网站+建设设计
  • 基于MATLAB的物理层算法原型验证
  • PHP网站开发程序员招聘一站式做网站哪家专业
  • 绵阳网站建设哪家好微信下拉小程序怎么关闭
  • 软件设计师——08 算法设计与分析
  • 炫酷企业网站网上买东西有哪些平台
  • DAY 42 Grad-CAM与Hook函数-2025.10.6