Tag: cpython
-
26.字节码优化
26.字节码优化 字节码优化是代码对象准备好执行之前的最后清理和细化阶段。 早期的编译器阶段决定程序的含义并发出指令。优化试图使这些指令更小、更简单或更快,同时保留 Python 语义。 例如: python id="u88pli" x = 1 + 2 可以像编写一样编译:```python id="k8v2ef" x = 3 但这: ``` python id = "lbwhpl" x = a + b ``` 无法折叠,因为 ` a ` 和 ` b ` 是运行时值。它们的类型可以定义任意行为 `+` 。 ## 26.1 在编译管道中的位置 优化发生在字节码生成之后或期间,具体取决于优化的类型。 ``` text id = "olp5um" source ↓ tokenization ↓ parsing ↓ AST ↓ symbol table ↓ instruction generation ↓ optimization ↓ assembly ↓ code object…
-
1.CPython 是什么
1.CPython 是什么 CPython 是 Python 编程语言的主要实现。这是大多数人从 python.org、包管理器、Linux 发行版、Homebrew、pyenv、Docker 映像或大多数云运行时安装 Python 时使用的实现。 CPython 有两个角色。 首先,它实现了Python语言。它定义了Python源代码如何解析、编译、执行以及连接到标准库。 其次,它充当 Python 生态系统的实际参考点。其他实现可能遵循语言规范,但 CPython 是大多数包、工具、调试器、分析器和 C 扩展测试的实现。 CPython 源代码树包含解释器、编译器、对象系统、运行时、C API、标准库、测试套件、文档和构建机制。官方开发人员指南将 CPython 描述为不断变化的代码库,并在源代码树中维护内部文档。 ([Python 开发人员指南][1]) 1.1 Python 语言与 CPython 实现 Python 是一种语言。 CPython 是该语言的一种实现。 这种区别很重要,因为 Python 语言描述了可见的行为:语法、名称、表达式、语句、对象、模块、异常、迭代、异步行为、导入等等。 CPython 描述了一种使该行为在真实机器上运行的具体方法。 像这样的 Python 程序:```python def add(a, b): return a + b print(add(2, 3)) ```是语言级别的Python。语言告诉我们 def 创建一个函数对象, return 返回一个值, + 调用加法语义,并且 print 编写文本表示。 CPython 通过较低级别的机制实现这些操作:```text source text ↓ tokens ↓…
-
36. 协程和异步
36. 协程和异步 协程是用于异步编程的可恢复计算。他们让 Python 代码暂停在 await 点,将控制权返回到事件循环,然后在等待的操作有结果时恢复。 协程类似于生成器,因为两者都在挂起期间保留执行状态。区别在于协议和目的。 生成器向迭代器使用者产生值。 协程等待其他异步操作并最终返回一个最终结果。 python id="o2u8bi" async def fetch(): data = await read() return data 呼唤 fetch() 不运行主体完成。它创建一个协程对象。```python id="9i5ai1" coro = fetch() ## 36.1 协程函数与协程对象 安 ` async def ` 语句创建一个协程函数。 调用协程函数会创建一个协程对象。 ``` python id = "rt8ky9" async def work (): return 42 coro = work () ``` 从概念上讲: ``` text id = "q71j65" work coroutine function object work () coroutine object suspended execution…
-
33. 属性查找
33. 属性查找 属性查找是用于计算表达式的运行时过程,例如:```python id="5ob0m9" obj.name 一个简单的属性表达式可以触发大量的机制: ``` text id = "b2zmcv" find the object 's type search the type and its base classes handle descriptors check the instance dictionary call custom attribute hooks return a value or raise AttributeError ```属性查找是动态的。结果可能取决于运行时对象、其类、其基类、其实例字典、描述符、元类和用户定义的挂钩。 ## 33.1 基本属性访问 表达式:```python id="67bubm" obj.x ```要求 CPython 查找名为的属性`"x"`在`obj`。 如果找到,则查找返回一个 Python 对象。 如果缺失,则会引发`AttributeError`。 例子:```python id="l53g0p" class C: pass obj = C() obj.x = 10 print(obj.x) ```作业商店`x`在实例字典中:```text id="kz7fxs" obj.__dict__["x"] = 10…
-
40. 模块和导入
40. 模块和导入 模块是 Python 代码加载、命名空间隔离和重用的基本单元。在 CPython 中,模块既是语言级对象,也是导入系统中的运行时记录。 在Python层面,模块是执行后得到的: python import math import os import json 每个导入的名称都绑定到模块对象、包对象、函数、类或其他导出对象。在CPython层面,导入是一个协调的过程,涉及字节码指令、导入钩子、模块规范、加载器、查找器、 sys.modules 、包路径、文件系统查找、字节码缓存、导入锁和模块执行。 导入系统不是简单的文件包含机制。它是一个运行时协议。 40.1 什么是模块 模块是类型的对象 module 。```python import sys print(type(sys)) print(sys. name ) 输出: text <class 'module'> sys 模块对象拥有一个字典。该字典是模块的全局命名空间。 python import math print(math. dict ["pi"]) print(math. dict ["sqrt"]) 对于名为 ` config . py ` : ``` python debug = True port = 8080 def connect (): return port ``` CPython 创建一个模块对象,准备其命名空间,在该命名空间内执行编译后的代码对象,并将生成的绑定保留在 ` config…
-
21. 符号表
21.符号表 解析生成 AST 后,CPython 执行范围分析。 此阶段构建符号表。 符号表记录名称在每个范围内的行为方式。它确定名称是本地名称、全局名称、自由名称、单元名称、参数名称、导入名称、注释名称还是从嵌套范围引用的名称。 对于这个来源:```python x = 10 def outer(): y = 20 def inner(): return x + y return inner 符号表阶段决定:```text x global from inner y free variable in inner y cell variable in outer inner local variable in outer outer global variable in module ```这种分析至关重要,因为字节码的生成依赖于它。加载局部变量使用与加载全局变量或闭包变量不同的字节码。 ## 21.1 在编译管道中的位置 符号表阶段位于 AST 构建和字节码生成之间。```text source ↓ tokenization ↓ parsing ↓ AST ↓ symbol table analysis ↓ compiler ↓…
-
14. 字符串、字节和 Unicode
14. 字符串、字节和 Unicode 文本和二进制数据是 Python 中独立的对象族。 str 代表 Unicode 文本。 bytes 表示不可变的二进制数据。 bytearray 表示可变的二进制数据。 这种分离是 Python 3 最重要的运行时设计选择之一。文本具有字符和编码。二进制数据有字节。 CPython 使用不同的对象布局、API 和不变量来实现这些概念。 14.1 文本与二进制数据 字符串是文本: python id="ly45du" s = "hello" bytes 对象是二进制数据: python id="1wh8hi" b = b"hello" 它们对于 ASCII 内容可能看起来相似,但它们是不同的类型。 python id="iqtxra" print(type("hello")) # <class 'str'> print(type(b"hello")) # <class 'bytes'> Python 不会隐式混合它们: python id="8bzmko" "hello" + b"world" # TypeError 这是故意的。组合文本和字节需要编码决策。```python id="8w5t7o" text = "hello" data = text.encode("utf-8") again = data.decode("utf-8") ##…
-
6. 从源代码到执行
6.从源代码到执行 CPython 不直接执行Python 源文本。它在第一个字节码指令运行之前通过几种内部表示形式转换源文本。 路径是:```text source text ↓ tokens ↓ parse tree ↓ abstract syntax tree ↓ symbol table ↓ code object ↓ frame ↓ bytecode evaluation ↓ object operations ## 6.1 源文本 输入以文本开始。 ``` python x = 1 + 2 print ( x ) ``` 在 CPython 执行此操作之前,它必须知道: ``` text where statements begin and end which characters form names which characters form numbers which indentation levels define blocks which…
-
7.Python对象模型
7.Python 对象模型 Python 对象模型是 CPython 的基础。 Python 中运行的所有内容最终都会成为对对象的操作:整数、字符串、列表、模块、函数、类、异常、框架,甚至编译后的代码。 在语言层面,Python 表示每个对象都有一个标识、一个类型和一个值。对象标识在创建后保持固定, is 比较身份,并且 id() 返回表示该身份的整数。 ([Python 文档][1]) CPython 使用 C 结构、对象头、引用计数、类型对象和操作槽来实现此模型。 7.1 对象、值和身份 Python 对象具有三个核心属性。 物业 意义 示例 身份 对象的稳定身份 id(x) 类型 对象的运行时类型 type(x) 价值 对象所表示的数据 42 , "abc" , [1, 2] 例子:```python x = [1, 2, 3] y = x print(x is y) # True print(type(x)) # <class 'list'> print(x) # [1, 2, 3] ` x ` 和 ` y…
-
31. 函数调用
31. 函数调用 函数调用是 CPython 中最重要的执行路径之一。一次调用同时连接多个系统:字节码执行、帧、参数绑定、描述符、方法、闭包、C API、引用计数、异常和返回处理。 一个简单的调用: python id="wauh11" result = f(1, 2) 在源代码级别看起来很小。在运行时,CPython 必须:```text id="rhdr0d" load the callable load the arguments choose the correct call protocol bind arguments to parameters create or initialize a frame if calling Python code execute the callee return a result or propagate an exception store the result ## 31.1 调用的含义 在 Python 中,调用表达式具有以下一般形式: ``` python id = "a7ltua" callable_object ( arguments ) ``` 括号之前的对象必须是可调用的。…
-
4. 口译员
章 标题 27 评估循环 28 框架 29 基于堆栈的执行 30 字节码指令 31 函数调用 32 方法调用 33 属性查找 34 异常处理 35 发电机 36 协程和异步 37 模式匹配 38 理解 39 封闭件和单元
-
11. 内存分配器
11. 内存分配器 CPython 不断分配内存。每个整数对象、列表对象、框架、元组、字典条目数组、字符串缓冲区、代码对象、异常、模块和函数都需要内存。分配器系统的存在是为了使这些分配快速、结构化、可调试且跨平台可移植。 CPython 不只使用一个分配器。它使用多个分配器域和层。小的 Python 对象通常会通过 CPython 的专用小对象分配器,而较大的缓冲区可能会通过平台分配器。 11.1 为什么 CPython 有自己的分配器 Python 程序创建许多短暂的对象。 python for i in range(1_000_000): x = (i, i + 1) 该循环分配许多元组对象和整数引用。如果每个小对象分配都直接进入系统 malloc ,开销会很大。 CPython 的分配器系统通过以下方式改进了这一点:```text serving small object allocations quickly grouping small allocations into arenas and pools reducing calls into the platform allocator supporting debug hooks separating allocator domains making object allocation behavior predictable enough for internals work ## 11.2 分配器域 CPython 将内存分配分为多个域。…
-
17. 整数、浮点数和复数
17. 整数、浮点数和复数 Python 的数字对象是具有专门实现的普通对象。它们参与与列表、字典、函数、类和模块相同的对象模型:每个值都有一个对象头、一个类型指针、引用计数行为和用于操作的类型槽。 主要的内置数字类型有: |类型 | Python 名称 |主要代表| | -------------- | ----------- | ------------------------ | | |整数| int |任意精度整数 | |布尔 | bool |单例子类 int | |浮点数| float | C双| |复杂| complex |一对C双打| 这些类型在 Python 级别看起来很简单,但每种类型都具有重要的运行时权衡。 17.1 数字对象就是对象 Python 整数通常不会作为原始 CPU 整数存储在 Python 变量中。 python x = 42 在 CPython 级别, x 指的是Python整型对象。 从概念上讲: text x ---> PyLongObject object header integer payload 对于浮点数和复数也是如此。 python a = 1.5 b…
-
39. 闭包和单元格
39. 闭包和单元格 闭包允许嵌套函数在封闭函数返回后使用该封闭函数中的变量。```python def make_adder(n): def add(x): return x + n return add add10 = make_adder(10) print(add10(5)) 输出: text 15 ```变量 n 属于 make_adder , 但 add 以后还是用它。 CPython 通过将捕获的变量移动到单元对象中来支持这一点。内部函数保留对这些单元格的引用。 39.1 嵌套函数 嵌套函数是在另一个函数内部定义的函数。```python def outer(): def inner(): return 1 return inner ```这 def inner 语句执行时 outer 运行。它创建一个函数对象并将其绑定到本地名称 inner 。 呼唤 outer() 返回该函数对象: python fn = outer() print(fn()) 函数对象为 inner 包含: text code object globals dictionary defaults keyword defaults annotations closure…
-
2. 从源代码构建 CPython
2. 从源代码构建 CPython 从源代码构建 CPython 为您提供了一个可以检查、修改、调试和测试的本地解释器。这是认真阅读内部结构之前的第一个实际步骤。 源代码构建可以让您执行打包的 Python 安装通常隐藏的操作:```text change interpreter code add debug prints inspect object layout run CPython tests use debug-only assertions trace reference counts debug crashes in C compare bytecode across builds CPython 存在于 Git 存储库中。正常的本地结帐如下所示: ``` bash git clone https : // github . com / python / cpython . git cd cpython ``` 该存储库包含解释器、标准库、测试、文档、构建文件和平台支持代码。 简化视图: ``` text cpython / Include / public and internal C…
-
44.类和元类
44.类和元类 类是一个运行时对象,它创建实例、存储属性、参与继承并通过 Python 对象模型定义行为。在 CPython 中,类是一个对象,其类型通常为 type 。 元类是类的类。它控制类对象的创建、初始化、表示和调用方式。 对于普通班级:```python id="g3fkzi" class User: pass print(type(User)) 输出: text id="iqt3cc" <class 'type'> ```这意味着 User 是一个对象,它的类型是 type 。 44.1 类是对象 类定义创建一个类对象。```python id="8zc5m6" class User: name = "anonymous" def hello(self): return "hello" 执行后,`User`是绑定在周围名称空间中的普通名称。 python id="rx6c1m" print(User) print(type(User)) print(User. name ) print(User. dict ) 类对象存储以下属性: text id="xxewj0" name qualname module dict bases mro methods descriptors class variables annotations ## 44.2 类定义即执行 类体是可执行代码。 ``` python id…
-
4. 阅读CPython C代码
4. 阅读 CPython C 代码 阅读 CPython C 代码需要同时使用两种思维模型。 第一个模型是普通的 C:结构、指针、宏、函数指针、引用所有权、分配、错误返回和条件编译。 第二个模型是Python的运行时模型:对象、类型、框架、异常、引用计数、描述符、迭代器、模块和字节码。 大多数 CPython 源文件都结合了两者。一行 C 代码可能看起来像普通的指针操作,但它通常编码 Python 语言规则。 4.1 从运行时不变量开始 中心不变量很简单: text id="vm1twr" Every Python value is represented as a PyObject pointer or a pointer to a struct whose first field is compatible with PyObject. 大多数 CPython 函数通过以下方式处理值 PyObject * 。```c id="x34zg4" PyObject *obj; 实际行为来自对象的类型: ``` c id = "y1r4zx" Py_TYPE ( obj ) ``` 类型决定哪些操作是有效的以及哪些 C 函数实现它们。 ##…
-
18. 代币化
18. 代币化 标记化是 CPython 编译管道中的第一个结构阶段。 它接收 Python 源文本并生成令牌流。解析器使用该令牌流并从中构建语法结构。 在这个阶段,CPython 还不知道程序是否有意义。它只识别词汇单元:名称、数字、字符串、运算符、换行符、缩进和文件结束标记。 分词器将其转变为: python def add(a, b): return a + b 成这样的流: text NAME "def" NAME "add" LPAR "(" NAME "a" COMMA "," NAME "b" RPAR ")" COLON ":" NEWLINE "\n" INDENT " " NAME "return" NAME "a" PLUS "+" NAME "b" NEWLINE "\n" DEDENT "" ENDMARKER "" 确切的标记名称和解析器接口因 CPython 版本而异,但核心思想是稳定的。 Python 标准库通过以下方式公开了 Python 级别的分词器: tokenize ,而 CPython 的解析器在内部使用自己的 C 分词器。公众 tokenize…
-
37.模式匹配
37. 模式匹配 模式匹配是Python的结构匹配系统。它是由 match 声明和 case 条款。```python match value: case 0: result = "zero" case [x, y]: result = x + y case {"name": name}: result = name case _: result = None 在 CPython 级别,模式匹配被编译为普通字节码加上专门的匹配指令。解释器评估主题,按顺序尝试每种情况,绑定成功匹配的名称,然后跳转到选定的正文。 ## 37.1 的`match`声明 一个 ` match ` 语句具有一个主题表达式和一个或多个案例。 ``` python match subject : case pattern : body case pattern if guard : body case _ : body ``` 主题表达式被评估一次。 ``` python match compute ():…
-
13. 内置对象实现
13. 内置对象实现 内置对象是 Python 核心类型背后的具体数据结构。它们是普通的 Python 对象,因为它们具有标识、类型、引用计数、支持的属性以及由类型槽定义的行为。它们很特别,因为它们的存储和操作是直接用 C 实现的。 本章给出了一个大致的地图。后面的章节将深入探讨字符串、列表、元组、字典、集合、数字、函数、模块和框架。 13.1 内置函数是类型对象 内置类型,例如 list , dict , 或者 int 本身就是一个Python对象。 python print(type(list)) # <class 'type'> print(type(dict)) # <class 'type'> print(type(int)) # <class 'type'> 实例指向其类型对象。 python xs = [1, 2, 3] print(type(xs)) # <class 'list'> C级:```text xs ---> PyListObject ob_refcnt ob_type ----> PyList_Type ob_size ob_item allocated ## 13.2 为什么用 C 实现内置函数 内置类型位于 Python 执行最热门的路径上。 常见的操作包括: ``` text integer arithmetic string hashing…
-
34.异常处理
34.异常处理 异常处理是 CPython 在操作无法正常完成时使用的控制流系统。它涵盖了明确的 raise 语句、失败的操作、失败的导入、失败的调用、生成器终止、上下文管理器清理、回溯构造以及通过帧的传播。 在源代码级别,异常如下所示: python id="cq34p8" try: value = risky() except ValueError: value = 0 在运行时,CPython 必须:```text id="o1a48o" execute the protected bytecode range detect failure record the active exception find a matching handler restore the frame stack to a valid state jump to handler bytecode run cleanup code propagate if no handler matches ## 34.1 什么是异常 异常是表示异常控制流的对象。 大多数例外是派生类的实例 ` BaseException ` 。 ``` python id = "m6c3g8"…
-
42. 导入锁
42. 导入锁 导入锁是防止不安全并发导入的同步机制。在 CPython 中,导入不仅仅是名称查找。他们可能会创建模块对象、改变 sys.modules ,执行任意Python代码,初始化扩展模块,更新包属性,编译源文件,读取字节码缓存,以及运行包初始化代码。 如果没有锁定,两个线程可以同时导入同一模块并观察到不一致的模块状态。 导入锁的存在是因为导入是执行,而执行会改变共享的运行时状态。 42.1 为什么导入需要锁定 考虑这个模块:```python id="ez8v8a" cache.py print("initializing cache") items = {} def get(key): return items[key] 现在考虑两个线程同时执行此操作: python id="2pklaf" import cache 如果没有同步,两个线程可能: text id="oy5qmp" create a module object insert or overwrite sys.modules["cache"] execute cache.py initialize items twice observe a partially initialized module bind different module objects ## 42.2 导入改变全局运行时状态 导入涉及共享状态。 重要的共享结构包括: ```text id="yxk2bl" sys.modules sys.meta_path sys.path sys.path_hooks sys.path_importer_cache parent package attributes module dictionaries…
-
28. 框架
#28. 框架 一帧是一次主动执行 Python 代码的运行时记录。当 CPython 调用 Python 函数、执行模块主体、运行类主体、恢复生成器或恢复协程时,它使用类似帧的执行记录来保存当前状态。 代码对象说明要执行什么。 框架表示当前执行的位置以及当前存在的值。 text code object = immutable instructions and metadata frame = mutable execution state for one run of that code 对于这个函数:```python def add(a, b): c = a + b return c 创建的框架 ` add ( 2 , 3 ) ` 包含当前参数值、局部变量槽、堆栈值、指令位置、异常状态以及到执行上下文的链接。 ## 28.1 框架为何存在 Python 程序可以同时有多个活动调用: ``` python def a (): return b () def b (): return c ()…
-
25. 字节码生成
25. 字节码生成 字节码生成是 CPython 将结构化语法转换为可执行虚拟机指令的阶段。 解析器构建 AST。 符号表决定范围行为。 然后编译器发出实现 Python 语义的字节码指令。 对于这个来源: python id="bjlwm8" def add(a, b): return a + b CPython 生成的字节码形状如下: text id="plhxq4" LOAD_FAST a LOAD_FAST b BINARY_OP + RETURN_VALUE 确切的指令名称和格式因 Python 版本而异,但模型保持稳定:```text id="ecx81v" bytecode is a low-level instruction stream executed by the CPython virtual machine 字节码生成发生在 AST 构建和范围分析之后。 ``` text id = "5v2o9v" source ↓ tokenization ↓ parsing ↓ AST ↓ symbol table ↓ bytecode generation ↓…
-
9. 引用计数
9. 引用计数 引用计数是 CPython 的主要内存管理机制。每个普通对象都带有当前指向它的强引用计数。当该计数降至零时,CPython 可以立即销毁该对象。 这种设计是 CPython 与许多其他语言运行时之间最明显的区别之一。 CPython 确实有一个循环垃圾收集器,但该收集器补充了引用计数。大多数对象是通过引用计数转换来回收的,而不是通过定期跟踪来回收。 9.1 核心思想 当某个对象拥有对它的引用时,Python 对象就会保持活动状态。 从概念上讲:```text object created reference count = 1 another owner stores the object reference count += 1 an owner releases the object reference count -= 1 reference count reaches 0 object is deallocated 例子: python x = [] y = x del x del y ```列表对象被创建并绑定到 x 。装订 y = x 创建对同一列表的另一个引用。正在删除 x 删除一个引用。正在删除 y 删除剩余的引用,因此可以销毁列表。…
-
12. 对象布局和类型槽
12. 对象布局和类型槽 CPython 将每个运行时值表示为一个对象。每个对象都有一个内存布局,每个对象的类型描述了如何解释该内存。 对象布局答案: text What fields exist inside this object? Where are the references to other Python objects? How large is one instance? Does the object have variable-sized trailing storage? Does the object participate in cyclic garbage collection? 类型槽答案:```text How is this object called? How is it deallocated? How does attribute lookup work? How does indexing work? How does addition work? How does iteration work? How is it…
-
3. 编译管道
章 标题 18 代币化 19 解析 20 AST 21 符号表 22 编译器通行证 23 代码对象 24 常量、名称和局部变量 25 字节码生成 26 字节码优化
-
22. 编译器通行证
22. 编译器通行证 编译器将 AST 和符号表信息转换为可执行代码对象。 早期阶段回答结构性问题。```text id="lfk0j9" tokenizer: What lexical units are in the source? parser: What syntax tree do these tokens form? symbol table: What scope does each name belong to? 编译器回答执行问题。 text id="ju2gsy" Which bytecode instructions should be emitted? Which constants belong in co_consts? Which names belong in co_names? Which local variables belong in co_varnames? Where should jumps go? How large can the evaluation stack grow? Which exception…
-
32. 方法调用
32. 方法调用 方法调用是通常以属性访问开始的函数调用。它们是 Python 对象模型的核心,因为大多数对象行为都是通过方法公开的。 方法调用例如: python id="l3owku" obj.method(arg) 看起来像一个操作,但 CPython 执行几个概念步骤:```text id="j39rdj" load obj look up attribute method bind obj if needed load arg call the resolved callable return result or raise exception ## 32.1 方法调用是属性访问加调用 源表达式: ``` python id = "xgyvgk" obj . method ( 10 ) ``` 可以理解为: ``` python id = "gg912h" tmp = obj . method tmp ( 10 ) ``` 这是正确的语义模型。首先进行属性查找。然后调用该查找的结果。 这个结果可能是: ``` text…
-
10. 垃圾收集器
10. 垃圾收集器 CPython 使用引用计数作为其主要内存管理机制。一旦最后一个强引用消失,引用计数就会销毁大多数对象。 引用计数有一个主要限制:它无法自行回收引用循环。 垃圾收集器的存在是为了找到容器对象的不可达循环并回收它们。它是引用计数的补充,而不是替代。 10.1 为什么引用计数需要帮助 仅当没有强引用指向对象时,引用计数才会达到零。 这适用于普通对象图:```python x = [] del x 它不适用于循环: ``` python a = [] b = [] a . append ( b ) b . append ( a ) del a del b ``` 删除两个名称后,列表仍然相互引用: ``` text list A ---> list B list B ---> list A ``` 它们的引用计数保持非零。但实时 Python 代码无法访问它们。 引用计数看到本地所有权。垃圾收集看到了可达性。 ## 10.2 垃圾收集器跟踪什么 CPython 不需要跟踪循环垃圾收集器中的每个对象。 不能包含对其他 Python 对象的引用的对象不能自行形成循环。示例包括许多整数、浮点数和简单字符串。 收集器主要跟踪类似容器的对象: ``` text…
-
23. 代码对象
23. 代码对象 代码对象是 CPython 的可执行 Python 代码的编译表示。 它包含字节码和元数据。解释器可以执行它,但代码对象本身不携带运行时状态,例如全局变量、默认参数、闭包单元或绑定方法。 对于这个来源: python def add(a, b): return a + b 函数对象 add 包含一个代码对象:```python code = add. code print(code.co_name) print(code.co_varnames) print(code.co_consts) print(code.co_names) ## 23.1 在编译管道中的位置 代码对象是编译的输出。 ``` text source text ↓ tokenization ↓ parsing ↓ AST ↓ symbol table ↓ compiler ↓ code object ↓ frame execution ``` 代码对象是编译器和解释器之间的切换。 编译器生成代码对象。 评估循环执行帧内的代码对象。 ## 23.2 代码对象与函数对象 函数对象包装了代码对象。 例子: ``` python def f ( x ): return…
-
35. 发电机
35. 发电机 生成器是可恢复的函数。正常函数以一个返回值开始、运行和结束。生成器可以启动、产生一个值、挂起其帧、稍后从同一指令位置恢复、产生另一个值,然后重复直到完成。 生成器函数是包含以下内容的任何函数体 yield 。 python id="j7t4va" def numbers(): yield 1 yield 2 yield 3 调用此函数不会立即运行主体。 python id="oyb9ik" g = numbers() 该调用创建一个生成器对象。当发电机恢复时,主体启动: python id="9sgmxp" print(next(g)) print(next(g)) print(next(g)) 输出: text id="wj3i3f" 1 2 3 在最后一个值之后,下一个简历将引发 StopIteration 。 35.1 生成器函数与生成器对象 生成器函数是用以下定义的可调用函数 def 。 生成器对象是调用生成器函数时返回的可恢复迭代器。```python id="nckwxg" def gen(): yield 1 print(gen) print(gen()) 从概念上讲: text id="hfh6hz" gen function object gen() generator object suspended execution state code object frame or frame-like state 这与普通函数不同: python…
-
24.常量、名称和局部变量
24. 常量、名称和局部变量 代码对象不会将 Python 源代码存储为文本。它存储紧凑表和通过索引引用这些表的字节码指令。 最重要的三个表是:```text id="mx2a9w" co_consts co_names co_varnames 对于这个函数: ``` python id = "wg80pj" def f ( a ): b = len ( a ) return b + 1 ``` CPython 存储: ``` text id = "jxh1ou" constants : None 1 names : len local variables : a b ``` 然后字节码指令通过索引引用这些条目。 ## 24.1 在编译管道中的位置 常量、名称和局部变量在编译期间组装。 ``` text id = "la84x8" AST ↓ symbol table ↓ compiler ↓ instruction…
-
30. 字节码指令
30. 字节码指令 字节码指令是 CPython 评估循环执行的操作。它们是经过解析、AST 构造、符号分析和编译后的 Python 代码的紧凑的解释器级形式。 Python 函数,例如:```python id="ya54lv" def add(a, b): return a + b 您可以使用以下命令检查该流 ` dis ` : ``` python id = "7yf46p" import dis def add ( a , b ): return a + b dis . dis ( add ) ``` 输出取决于 Python 版本,但通常显示如下指令: ``` text id = "ot1vx4" LOAD_FAST LOAD_FAST BINARY_OP RETURN_VALUE ``` 这些指令是 CPython 虚拟机的词汇表。 ## 30.1 什么是字节码指令 字节码指令告诉解释器执行一个小操作。 示例: ```…
-
8.PyObject 和 PyVarObject
#8. PyObject 和 PyVarObject PyObject 和 PyVarObject 是 CPython 对象背后的基本布局。它们不是 Python 类。它们是 C 级结构约定,允许运行时通过通用指针类型处理许多不同的对象实现。 在运行时,CPython 中的大多数对象引用表示为:```c PyObject * ## 8.1 公共对象头 一个简化的 ` PyObject ` 看起来像这样: ``` c typedef struct { Py_ssize_t ob_refcnt ; PyTypeObject * ob_type ; } PyObject ; ``` 真正的定义使用宏和依赖于构建的字段,特别是在调试构建、跟踪构建和现代 CPython 版本中。但本质思想是稳定的: ``` text PyObject reference count type pointer ``` 引用计数跟踪所有权。 类型指针告诉 CPython 对象的行为方式。 每个普通的 CPython 对象都以这个公共标头开始。因此,运行时可以接收 ` PyObject *` 并在编译时不知道完整的具体结构的情况下检查其类型。 ## 8.2 为什么每个对象都以相同的方式启动 考虑下面的 Python 代码:…
-
29. 基于堆栈的执行
29. 基于堆栈的执行 CPython 使用堆栈机执行大多数字节码。堆栈机使用隐式操作数堆栈,而不是在每条指令中命名显式源寄存器和目标寄存器。 在 CPython 中,该堆栈属于当前帧。它存储对 Python 对象的引用。字节码指令推送对象、弹出对象、检查对象、替换对象以及使用对象计算新结果。 一个简单的表达: python id="kz6m7x" x = a + b 在概念上执行为: text id="eurdpx" LOAD_FAST a push a LOAD_FAST b push b BINARY_OP + pop b and a, push result STORE_FAST x pop result into local x 指令流没有说: text id="ardj57" add local_a, local_b, local_x 相反,它说:```text id="jzzlmf" load a load b add top two stack values store result ## 29.1 为什么 CPython 使用堆栈机 堆栈机为字节码提供了紧凑的表示形式。许多指令不需要显式操作数位置,因为它们在堆栈顶部操作。 例如:…
-
27.评估循环
#27.评估循环 The evaluation loop is the central execution engine of CPython. It takes a compiled code object, executes its bytecode instructions, and produces a result or an exception. At a high level, CPython execution looks like this:```text Python source ↓ tokens ↓ parser ↓ AST ↓ symbol table ↓ compiler ↓ code object ↓ frame ↓ evaluation loop ↓ Python result or exception 评估循环存在于 CPython 的解释器实现中。从历史上看,密钥文件一直是 ` Python / ceval…
-
3. 存储库布局
3. 存储库布局 CPython 存储库围绕解释器的主要子系统进行组织:对象实现、运行时机制、编译器管道、解析器、内置模块、标准库、测试、文档和平台构建文件。 好的第一步是将源代码树视为职责地图。```text cpython/ Include/ Objects/ Python/ Parser/ Modules/ Lib/ Programs/ Tools/ Doc/ Grammar/ PC/ PCbuild/ Mac/ ## 3.1 顶层结构 |目录 |主要角色| | ----------- | --------------------------------------------------- | |`Include/`|公共、内部和私有 C 头文件 | |`Objects/`|核心对象类型的实现 | |`Python/`|运行时、编译器、解释器循环、初始化 | |`Parser/`|分词器和解析器支持代码 | |`Grammar/`|语法输入文件 | |`Modules/`|用 C 编写的内置和扩展模块 | |`Lib/`| Python标准库| |`Lib/test/`| CPython 回归测试套件 | |`Programs/`|可执行入口点| |`Tools/`|开发人员和构建工具| |`Doc/`|文档来源| |`PC/`| Windows 特定的源文件和配置文件 | |`PCbuild/`| Windows 构建系统 | |`Mac/`| macOS 特定支持 | 内部阅读最重要的目录是:```text Include/ Objects/ Python/…
-
5. 运行时模型
5. 运行时模型 CPython 运行时是进程启动之后、Python 代码执行完成之前存在的机制。它拥有解释器状态、线程状态、模块、内置函数、内存分配器、异常状态、导入状态、帧、挂起调用、信号处理和关闭行为。 Python 程序看起来是作为一系列语句运行的。 CPython 在分层运行时系统中运行它。```text operating system process CPython runtime interpreter state thread state frame stack executing code object bytecode instructions object operations ## 5.1 进程、运行时、解释器、线程、框架 正在运行的 CPython 程序有多个嵌套的执行单元。 | 单位 | 意义 | | ---------- -- | -------------------------------------------------------------------------------- | | 流程 | 包含 CPython 可执行文件或嵌入式运行时的操作系统进程 | | 运行时 | 跨进程共享全局 CPython 状态 | | 口译 | 运行时内的隔离 Python 解释器状态 | | 线程状态 | 一个解释器的每线程执行状态 |…
-
1. 方向
章 标题 1 CPython 是什么 2 从源代码构建 CPython 3 存储库布局 4 阅读 CPython C 代码 5 运行时模型 6 从源代码到执行
-
19. 解析
19. 解析 解析是将令牌流转换为语法结构的阶段。 分词器识别词汇单元。解析器识别语法结构。它决定一个标记序列是否是一个有效的Python程序,并且当它有效时,构建一个抽象语法树。 对于这个来源: python def add(a, b): return a + b 标记器生成标记,例如: text NAME "def" NAME "add" LPAR "(" NAME "a" COMMA "," NAME "b" RPAR ")" COLON ":" NEWLINE "\n" INDENT " " NAME "return" NAME "a" PLUS "+" NAME "b" NEWLINE "\n" DEDENT "" ENDMARKER "" 解析器给出这些标记结构: text Module FunctionDef name="add" arguments arg "a" arg "b" body Return BinOp Name "a" Add Name "b" 差异很重要。标记化知道 def…
-
43. 描述符
43. 描述符 描述符是一个控制另一个对象的属性访问的对象。描述符是 Python 对象模型背后的主要机制之一。他们解释了方法如何绑定到实例,如何 property 工作原理,如何 staticmethod 和 classmethod 工作原理、槽的工作原理以及有多少 CPython 级别的类型操作连接到 Python 级别的语法。 在语言级别,描述符是定义以下一个或多个方法的任何对象: python id="x7hcrg" __get__(self, obj, objtype=None) __set__(self, obj, value) __delete__(self, obj) 一个对象与 __get__ 是一个描述符。 一个对象与 __set__ 或者 __delete__ 是一个数据描述符。 非数据描述符和数据描述符之间的区别控制着查找优先级。 43.1 为什么描述符存在 Python 属性访问看起来很简单: python id="96l8i5" obj.name 但这个表达式并不意味着“读取一个名为 name 从记忆中。” 这意味着:```text id="drqc0j" ask the object's type how attribute lookup works search descriptors and dictionaries in a defined order possibly call descriptor methods return the resulting…
-
41. 套餐
41. 包 包是一个可以包含其他模块的模块。在 CPython 中,包不是一个单独的对象类别。它仍然是一个模块对象,但它具有导入元数据,告诉导入系统在哪里查找子模块。 在Python级别,这个目录可以是一个包: text app/ __init__.py config.py server.py 您可以将其导入为: python import app import app.config from app.server import run 重要的规则很简单:```text A package is a module with submodule search locations. ## 41.1 包是模块 包对象具有类型 ` module ` 。 ``` python import email print ( type ( email )) print ( email . __name__ ) ``` 输出: ``` text < class 'module' > email ``` 就像任何其他模块一样,包也有一个模块字典。 ``` python import email…
-
15. 列表、元组和数组
15.列表、元组和数组 列表、元组和类似数组的对象表示有序集合。它们都支持索引访问,但具有不同的存储模型、可变性规则和性能权衡。 列表是对象引用的可变序列。 元组是对象引用的不可变序列。 类似数组的对象存储紧凑类型的数据或公开连续的缓冲区。 这些区别很重要,因为 CPython 容器存储引用,除非该类型是专门为原始存储设计的。 15.1 有序集合 Python 有多种有序集合类型。 |类型 | 可变 |商店 |主要用途| | ------------- | -------------:| ----------------- | ------------------------------------------- | | list | 是的 |对象参考|一般可变序列 | | tuple | 没有 |对象参考|固定记录、不可变组 | | array.array | 是的 |原始类型值 |紧凑的数字存储 | | bytes | 没有 |原始字节 |不可变的二进制数据 | | bytearray | 是的 |原始字节 |可变二进制数据 | | memoryview |视图相关 |原始缓冲区视图 |零拷贝缓冲区访问 | 列表和元组可以存储任何类型的对象: python xs = [1, "two",…
-
16. 字典和集合
16. 字典和集合 字典和集合是 CPython 的主要哈希表容器。字典将键映射到值。集合存储没有关联值的键。 它们在 Python 中无处不在:```text id="cp7dx7" module globals class namespaces instance attributes keyword arguments function annotations import caches memoization tables membership indexes deduplication sets ## 16.1 字典语义 字典将可散列键映射到值 。``` python id = "cvrlq9" d = { "name" : "Ada" , "age" : 36 , } print ( d [ "name" ]) # Ada ``` 键必须是可散列的 。 值可以是任何对象 。``` python id = "mb4tqx" d = {} d […
-
20. AST
#20. AST 抽象语法树,通常称为AST,是Python源代码解析后的结构化表示。 标记器生成扁平的标记流。解析器识别语法。 AST 将结果记录为语句和表达式的树。 对于这个来源: python x = 1 + 2 AST 的形状如下:```text Module Assign targets: Name id="x", ctx=Store value: BinOp left: Constant value=1 op: Add right: Constant value=2 ## 20.1 在编译管道中的位置 AST 位于解析和编译器分析之间。 ``` text source bytes ↓ tokenization ↓ parsing ↓ AST ↓ AST validation ↓ symbol table ↓ compiler ↓ code object ↓ bytecode execution ``` 解析器构建 AST 。后续阶段会消耗它。 AST 回答以下问题: ``` text What statements…
-
38.理解
38. 理解 推导式是一种紧凑的语法,用于从另一个可迭代对象构建容器或类似生成器的迭代器。 CPython 将它们实现为具有自己的执行范围的编译代码对象。 常见形式: python [x * 2 for x in xs] {x * 2 for x in xs} {x: x * 2 for x in xs} (x * 2 for x in xs) 这些对应于:```text list comprehension set comprehension dict comprehension generator expression ## 38.1 列表理解 列表理解急切地构建一个列表。 ``` python ys = [ x * 2 for x in xs ] ``` 从概念上讲: ``` python ys = [] for…
-
2. 对象和内存
章 标题 7 Python 对象模型 8 PyObject 和 PyVarObject 9 引用计数 10 垃圾收集器 11 内存分配器 12 对象布局和类型槽 13 内置对象实现 14 字符串、字节和 Unicode 15 列表、元组和数组 16 词典和词典集 17 整数、浮点数和复数