CPython 符号表探秘:使用 symtable 模块剖析编译器的名字作用域
2026/9/9 19:11:36 网站建设 项目流程

CPython 符号表探秘:使用 symtable 模块剖析编译器的名字作用域

【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython

符号表(symbol table)是 CPython 编译流水线中连接 AST 与字节码的关键中间产物,它负责为源码中每一个标识符计算出其作用域(scope)归属。本文以官方文档 Doc/library/symtable.rst 为主体,结合 Lib/symtable.py、Python/symtable.c 与 Modules/symtablemodule.c 等实现源码,系统讲解symtable()入口、SymbolTable/Symbol/Function/Class完整 API,以及底层标志位的作用域编码原理,最终让你能编写代码精确查询任意标识符是局部变量、全局变量、闭包自由变量还是类型参数,并掌握python -m symtable命令行工具。


1. 符号表:编译流水线中承上启下的“作用域计算器”

在 CPython 的编译流程里,源码先被解析为 AST,再经过符号表分析,最后才生成字节码。符号表的作用是:在生成字节码之前,为每个代码块(module、function、class 等)计算其中每个标识符的作用域。在 Python/compile.c 中可以看到编译核心对符号表构建函数的直接调用:

c->c_st = _PySymtable_Build(mod, filename, &c->c_future);

也就是说,Python 解释器在真正把代码“编译成指令”前,就已经通过_PySymtable_Build()(定义于 Python/symtable.c)构建好整棵符号表树,后续的代码生成器据此决定把某个名字编译成LOAD_FASTLOAD_GLOBALLOAD_DEREF还是LOAD_CLOSURE等指令。

symtable模块正是向 Python 开发者开放这棵内部符号表的接口。模块的文档化说明与入口位于 Doc/library/symtable.rst,纯 Python 侧实现为 Lib/symtable.py,真正的构建逻辑则在 C 扩展模块_symtable(Modules/symtablemodule.c)与编译器符号表引擎 Python/symtable.c 中。公开 API 由 Lib/symtable.py 的__all__明确列出:

__all__ = ["symtable", "SymbolTableType", "SymbolTable", "Class", "Function", "Symbol"]

2. 生成符号表:symtable() 函数

2.1 函数签名与参数语义

symtable模块提供唯一的入口函数,其完整签名如下:

symtable.symtable(code, filename, compile_type, *, module=None)
  • code:待分析的 Python 源码。可以是一个str、一个bytes对象,或一个 AST 对象(与内置函数compile()的输入一致)。三种输入形态在 Lib/symtable.py 的symtable()中被原样转发给_symtable.symtable()

    • 传入str/bytes时,C 层通过_Py_SourceAsString()统一转为 UTF-8 字符串再送入_Py_SymtableStringObjectFlags()(见 Modules/symtablemodule.c);
    • 传入 AST 对象时(这是文档中以versionchanged:: next标注、尚在开发分支上的新能力),C 层走symtable_from_ast()路径:先用PyAST_obj2mod()把 AST 对象转成内部mod_ty,经_PyAST_Validate()校验、再由_PyFuture_FromAST()提取__future__特性,最后才_PySymtable_Build()(见 Modules/symtablemodule.c)。这意味着即使没有源码文本、只有程序化拼装的 AST 节点,也能生成符号表——前提是 AST 已通过ast.fix_missing_locations()补齐位置信息。
  • filename:包含该代码的“文件名”。它更像一个用于报错定位和警告归类的标签,并不要求磁盘上真的存在该文件。底层参数使用unicode_fs_decoded转换(见 Modules/symtablemodule.c),因此除str外也接受表示路径的bytes

  • compile_type:语义与compile()mode参数一致,只接受'exec''eval''single'三个字符串。C 层对三者逐一比对并映射到内部输入模式(Py_file_input/Py_eval_input/Py_single_input),若传入其它值则抛出ValueError(见 Modules/symtablemodule.c):

    PyErr_SetString(PyExc_ValueError, "symtable() arg 3 must be 'exec' or 'eval' or 'single'");
  • module(关键字专属,3.15 新增):指定模块名。其用途是让语法警告(SyntaxWarning)能够按模块名精确过滤——参考 Doc/library/symtable.rst 对 warning-filter 的引用。当源码中存在需要告警的写法(如is与字面量比较)时,符号表构建阶段会触发SyntaxWarning;显式传入模块名后,warnings.filterwarnings(..., module=...)才能识别这些警告的来源模块。

2.2 一次最小的调用

>>> import symtable >>> table = symtable.symtable("def f(x): return x + 1", "example.py", "exec") >>> table.get_type() 'symbol table type... module' # 实际返回 SymbolTableType.MODULE 成员(见下节)

返回的是整棵符号表树的顶层(module 级)SymbolTable对象,通过它可继续向下遍历嵌套的类、函数与类型作用域。


3. SymbolTableType:七种符号表类型

SymbolTableType是定义于 Lib/symtable.py 的StrEnum枚举,描述了SymbolTable对象的类型,取值同时是字符串。SymbolTable.get_type()的返回值从 3.13 起即该枚举的成员;在更早版本(3.12)里是裸字符串'annotation''TypeVar bound''type alias''type parameter',且官方明确提示返回字符串的具体字面值将来可能变化,应优先使用SymbolTableType成员而非硬编码字符串做比较

枚举成员字符串值适用场景
SymbolTableType.MODULE"module"模块级顶层符号表
SymbolTableType.FUNCTION"function"函数或方法体符号表
SymbolTableType.CLASS"class"类体符号表
SymbolTableType.ANNOTATION"annotation"from __future__ import annotations生效时用于承载注解的作用域
SymbolTableType.TYPE_ALIAS"type alias"type语句(PEP 695 类型别名)的符号表
SymbolTableType.TYPE_PARAMETERS"type parameters"泛型函数/泛型类/泛型类型别名的类型参数作用域
SymbolTableType.TYPE_VARIABLE"type variable"单个类型变量的 bound、约束元组或默认值所在的作用域(对应TypeVar/TypeVarTuple/ParamSpec,其中后两者不支持 bound 或约束元组)

SymbolTableType枚举整体自 3.13 加入。

3.1 与 C 层 block 类型的一一对应

这七种“逻辑类型”并非 Python 层凭空捏造,而是与符号表引擎内部的实际 block 类型严格对应。在 Modules/symtablemodule.c 中,_symtable模块把 C 端的类型常量以TYPE_*名称导出;这些常量来自 Include/internal/pycore_symtable.h 中定义的枚举_block_type,且TypeAliasBlockTypeParametersBlockTypeVariableBlock等新型块在 Python/symtable.c 中由symtable_enter_block()(Python/symtable.c)实际创建。例如泛型类class GenericMine[T]会先进入一个TypeParametersBlock,其内部才是常规的ClassBlock。因此你看到的“type parameters 在外、function/class 在内”的嵌套结构,正反映了代码的语法层级。


4. SymbolTable:遍历代码块作用域

SymbolTable表示一个“代码块”的命名空间表,其构造函数不是公开的,只能通过symtable()或父表的get_children()获得。文档位于 Doc/library/symtable.rst 的 “Examining Symbol Tables” 一节。

4.1 表级属性方法

方法返回说明
get_type()SymbolTableType表类型,取值见第 3 节
get_id()int表的唯一标识符(自增整数,测试断言其大于 0)
get_name()str表名:类表返回类名,函数表返回函数名,模块表返回'top'type parameters作用域返回其承载的类/函数/类型别名名,type alias作用域返回别名本身的名字,TypeVar bound 作用域返回该TypeVar的名字
get_lineno()int该块首行行号(模块表为 0)
is_optimized()bool局部变量是否可被优化(True表示局部量可编译为快速指令,见 Lib/symtable.py,本质上等价于“这是一个函数块”)
is_nested()bool是否为嵌套的类或函数(如定义在函数体内的内部函数)
has_children()bool块内是否含有嵌套命名空间
get_identifiers()视图对象表中全部符号名,行为符合 dict-views 语义
lookup(name)Symbol按名查询符号;名字不存在时抛出KeyError(见测试 Lib/test/test_symtable.py)
get_symbols()list[Symbol]返回表中全部符号对象列表
get_children()list[SymbolTable]返回嵌套子符号表列表

对象还具备可读性不错的repr(见 Lib/symtable.py):

<SymbolTable for module example.py> <Function SymbolTable for f in example.py>

4.2 底层 lookup 的缓存与命名空间挂接

观察 Lib/symtable.py 的实现可以发现两个值得注意的机制:

  1. 惰性缓存SymbolTable.lookup()第一次查询某名字时,才去底层读取该符号的标志位并构造Symbol对象,随后缓存在self._symbols字典中;get_symbols()是对get_identifiers()逐个lookup()的结果。
  2. 命名空间自动挂接lookup()内部还会调用__check_children(),把子表中与该符号同名的那些SymbolTable收集起来挂到符号对象上——这正是后面Symbol.is_namespace()/get_namespaces()的数据来源:一个名字(如def namespace_test)若被多次def,就会关联到多个命名空间。

顶层表对象本身由工厂SymbolTableFactory借助WeakValueDictionary做内存复用,保证同一底层表在多次访问时返回相同的 Python 包装对象(见 Lib/symtable.py)。


5. Function 与 Class:针对函数、类的特化接口

FunctionClass都继承自SymbolTable(定义于 Lib/symtable.py),由工厂按底层 block 类型自动选择构造:

  • 底层类型为函数 →Function
  • 底层类型为类 →Class
  • 其余一律是普通SymbolTable

Class目前没有额外方法;Function则基于标识符的标志位过滤,给出六类“角色名单”,全部返回元组,且结果会被缓存(首次计算后保存到实例属性,见 Lib/symtable.py):

方法返回内容底层过滤依据
get_parameters()形参名元组标志含DEF_PARAM
get_locals()局部变量名元组scope 为LOCALCELL
get_globals()全局名元组scope 为GLOBAL_IMPLICITGLOBAL_EXPLICIT
get_nonlocals()显式nonlocal声明的名字元组标志含DEF_NONLOCAL
get_frees()自由(闭包)变量名元组scope 恰为FREE
get_cells()cell 变量名元组(3.15 新增scope 恰为CELL

术语 “free (closure) variables” 与 “cell variables” 都指向 Doc/glossary.rst 中的“闭包变量”词条,二者的精确差别见下文 Symbol 一节。

5.1 一个贯穿测试样例

以 Lib/test/test_symtable.py 中反复使用的经典样例spam函数为例:

def spam(a, b, *var, **kw): global bar global some_assigned_global_var some_assigned_global_var = 12 bar = 47 some_var = 10 x = 23 glob def internal(): return x def other_internal(): nonlocal some_var some_var = 3 return some_var return internal

测试 Lib/test/test_symtable.py 验证了以下结果,可以作为理解各方法语义的对照表:

sorted(func.get_parameters()) # ['a', 'b', 'kw', 'var'] sorted(func.get_locals()) # ['a', 'b', 'internal', 'kw', 'other_internal', 'some_var', 'var', 'x'] sorted(func.get_globals()) # ['bar', 'glob', 'some_assigned_global_var'] self.internal.get_frees() # ('x',) self.spam.get_cells() # ('some_var', 'x',)

注意观察点:

  • 形参a/b/var/kw同时既在 parameters 中,也被算作 locals(它们都编译为本地快速变量);
  • 只读未赋值的glob是“隐式全局”(is_declared_global()False),而经global声明后赋值的barsome_assigned_global_var是“显式全局”;
  • 内部函数internal引用了外层spamx,于是对internal而言x是 free 变量(FREE),对spam而言xsome_var变成了 cell 变量(CELL)。

6. Symbol:查看单个标识符的作用域与标志位

Symbol是符号表中对应“源码中一个标识符”的条目,构造函数同样不公开,只能经由SymbolTable.lookup()/get_symbols()获得。

6.1 顶层:标志位与作用域的位级编码

要真正读懂Symbol的所有查询方法,需要先了解 C 层对符号信息的压缩存储。见 Include/internal/pycore_symtable.h:

#define DEF_GLOBAL 1 /* global stmt */ #define DEF_LOCAL 2 /* assignment in code block */ #define DEF_PARAM (2<<1) /* formal parameter */ #define DEF_NONLOCAL (2<<2) /* nonlocal stmt */ #define USE (2<<3) /* name is used */ #define DEF_FREE_CLASS (2<<5) /* free variable from class's method */ #define DEF_IMPORT (2<<6) /* assignment occurred via import */ #define DEF_ANNOT (2<<7) /* this name is annotated */ #define DEF_COMP_ITER (2<<8) /* this name is a comprehension iteration variable */ #define DEF_TYPE_PARAM (2<<9) /* this name is a type parameter */ #define DEF_COMP_CELL (2<<10) /* this name is a cell in an inlined comprehension */ #define DEF_BOUND (DEF_LOCAL | DEF_PARAM | DEF_IMPORT) #define SCOPE_OFFSET 12 #define SCOPE_MASK (DEF_GLOBAL | DEF_LOCAL | DEF_PARAM | DEF_NONLOCAL)

其中DEF_BOUND是“该名字在块内被绑定(赋值/形参/导入)”的总标志组合。作用域类型(5 种)被编码在高 12 位起的低 4 位中:_get_scope(flags)(flags >> SCOPE_OFFSET) & SCOPE_MASK(Lib/symtable.py 忠实复刻了 C 端的SYMBOL_TO_SCOPE()宏)。_symtable模块导出这些常量:USEDEF_GLOBALDEF_NONLOCALDEF_LOCALDEF_PARAMDEF_TYPE_PARAMDEF_FREE_CLASSDEF_IMPORTDEF_BOUNDDEF_ANNOTDEF_COMP_ITERDEF_COMP_CELLSCOPE_OFFSCOPE_MASK,以及五种作用域FREELOCALGLOBAL_IMPLICITGLOBAL_EXPLICITCELL(见 Lib/symtable.py)。

Symbolrepr会同时展示作用域与标志位,便于调试:

<symbol 'glob': GLOBAL_IMPLICIT, USE> <symbol 'bar': GLOBAL_EXPLICIT, DEF_GLOBAL|DEF_LOCAL> <symbol 'a': LOCAL, DEF_PARAM> <symbol 'x': FREE, USE>

(以上输出与 Lib/test/test_symtable.py 中test_symbol_repr的断言一致。)

6.2 查询方法一览

方法版本语义(True当且仅当…)
get_name()返回符号名
is_referenced()符号在其块内被使用过(标志含USE
is_imported()符号由 import 语句产生(含DEF_IMPORT
is_parameter()符号是形参(含DEF_PARAM
is_type_parameter()3.14符号是类型参数(含DEF_TYPE_PARAM
is_global()符号是全局(scope 为隐式/显式全局;模块作用域中已绑定的名字也算全局,见 Lib/symtable.py)
is_nonlocal()符号被nonlocal声明(含DEF_NONLOCAL
is_declared_global()符号被global语句显式声明(scope 恰为GLOBAL_EXPLICIT
is_local()符号局部于其块(scope 为LOCAL/CELL;模块作用域中已绑定的名字也算局部,因此模块级名字同时 is_local 且 is_global
is_annotated()3.6符号带注解(含DEF_ANNOT
is_free()符号在块内被引用但未被赋值(scope 恰为FREE,即闭包引用外部变量)
is_cell()3.15符号是 cell 变量(scope 恰为CELL;变量因被嵌套作用域引用而“晋升”为 cell)
is_free_class()3.14类作用域符号站在方法视角上是“自由的”(含DEF_FREE_CLASS
is_assigned()符号在块内被赋值(含DEF_LOCAL
is_comp_iter()3.14符号是推导式的迭代变量(含DEF_COMP_ITER
is_comp_cell()3.14符号是内联推导式中的 cell(含DEF_COMP_CELL
is_namespace()名字绑定引入了新命名空间(通常用作def/class的目标名)
get_namespaces()绑定到该名字的全部命名空间表列表
get_namespace()绑定的唯一命名空间;若绑定 0 个或多个则抛ValueError

关于get_namespace()/get_namespaces()的边界行为,Lib/test/test_symtable.py 给出了清晰验证:源码中若def namespace_test(): pass出现两次,top.lookup("namespace_test").get_namespaces()长度为 2,此时get_namespace()ValueError;反之模块级glob = 42这种无命名空间绑定,get_namespaces()为空,get_namespace()同样抛ValueError。只绑定一处时才正常返回子表。

6.3 is_namespace() 的经典示例

Doc/library/symtable.rst 中给出了is_namespace()的官方演示(使用"string"作为无害的文件名标签):

>>> table = symtable.symtable("def some_func(): pass", "string", "exec") >>> table.lookup("some_func").is_namespace() True

注意一个名字可以同时绑定多个对象:即便is_namespace()True,该名字也可能同时绑定到不引入新命名空间的普通值(如intlist)。

6.4 is_free_class() 的边界语义

类作用域与函数/方法作用域的交互历来容易困惑,Doc/library/symtable.rst 用下面的例子解释is_free_class()(3.14 新增):

def f(): x = 1 # function-scoped class C: x = 2 # class-scoped def method(self): return x

C.method的视角看,类作用域里的符号x会被视为“自由的”(DEF_FREE_CLASS标志),因此运行时method返回的是外层函数f中的1,而不是类的2。这正是 Python “类作用域不参与闭包解析”规则的符号表体现——Lib/test/test_symtable.py 验证了class_A.lookup('x')repr<symbol 'x': LOCAL, DEF_LOCAL|DEF_FREE_CLASS>

6.5 推导式的特殊处理:is_comp_iter / is_comp_cell

列表/集合/字典推导式与生成器表达式在符号表中同样有独立的存在。底层符号条目会被打上DEF_COMP_ITER(推导式迭代变量)与DEF_COMP_CELL(内联推导式中晋升为 cell 的变量)标志。测试 Lib/test/test_symtable.py 展示了两组差异:

<symbol 'x': LOCAL, USE|DEF_LOCAL|DEF_COMP_ITER> # [x for x in [1]] <symbol 'x': CELL, DEF_LOCAL|DEF_COMP_ITER|DEF_COMP_CELL> # [(lambda: x) for x in [1]]

当推导式的迭代变量被内部 lambda 捕获时,它就从普通局部升级为 cell——这与 Python 3.12 起“推导式内联”的字节码优化策略(见 Doc/whatsnew)直接相关。

顺带一提:推导式/生成器在符号表中表现为名为<genexpr><lambda>的匿名FUNCTION块;生成器表达式的迭代源还会出现名为.0的合成形参,见测试 Lib/test/test_symtable.py。


7. 泛型与注解时代的新型作用域(3.12–3.14)

CPython 从 3.12 引入 PEP 695 类型参数语法后,符号表的get_type()返回类型经历了两次演进:

  • 3.12:开始把'annotation''TypeVar bound''type alias''type parameter'作为可能返回值;
  • 3.13:返回值改为SymbolTableType枚举成员。

在 Lib/test/test_symtable.py 的test_type中可以看到这些新块如何嵌套(源码见该文件开头TEST_CODE):

type Alias = int type GenericAlias[T] = list[T] def generic_spamT: ... class GenericMine[T: int, U: (int, str) = int]: ...

对应的断言为:

top.get_type() # SymbolTableType.MODULE("module") Alias.get_type() # "type alias" —— type 别名体 GenericAlias.get_type() # "type parameters" —— 泛型别名的最外层 GenericAlias_inner.get_type() # "type alias" —— 内部的别名体 generic_spam.get_type() # "type parameters" —— 泛型函数外壳 generic_spam_inner.get_type() # "function" —— 真正的函数体 GenericMine.get_type() # "type parameters" —— 泛型类外壳 GenericMine_inner.get_type() # "class" —— 真正的类体 T / U 所在表.get_type() # "type variable" —— 单个类型变量

由此可见一个泛型声明在符号表里会拆成内外两层甚至三层块:外层TYPE_PARAMETERS[T],里层才是FUNCTION/CLASS/TYPE_ALIAS实体;带约束(T: int)、默认值(U: ... = int)的单个类型参数还各有一个TYPE_VARIABLE块承载其 bound/约束/默认值。这也解释了get_name()在类型作用域上“返回底层类/函数/类型别名的名字”的规则。

7.1 注解作用域与 get_children() 的偏移

from __future__ import annotations(推迟注解求值)生效时,注解内容会产生ANNOTATION类型的子块。从 Python/symtable.c 等处的symtable_enter_block(..., AnnotationBlock, ...)调用可见,这类块是真实进入符号表树的——因此遍历get_children()时,注解作用域会作为一个合法的子表出现,调试时需要注意其在子表序列中的位置。

7.2 类型参数查询:is_type_parameter()

对表内任意符号可用Symbol.is_type_parameter()(3.14 新增)直接判断其是否来自类型参数声明,例如GenericMine中符号Trepr为:

<symbol 'T': LOCAL, DEF_LOCAL|DEF_TYPE_PARAM>

底层依据是DEF_TYPE_PARAM标志位(Include/internal/pycore_symtable.h)。


8. 命令行用法:python -m symtable

3.13起,symtable可以作为脚本从命令行直接执行,这对于快速“解剖”一个源文件的作用域结构非常方便(见 Doc/library/symtable.rst 的 “Command-Line Usage” 一节):

python -m symtable [infile...]
  • 为列出的每个 Python 源文件生成符号表并打印到stdout
  • 不指定输入文件时,从 stdin 读取内容
  • 显式传入-同样表示从 stdin 读取(Lib/symtable.py 中'-'与空参数行为一致,测试见 Lib/test/test_symtable.py)。

输出为缩进嵌套的树状文本(实现于 Lib/symtable.py 的main()),每一层先打印块头再列出符号及其 scope/标志:

symbol table for module from file 'sample.py': local symbol 'glob': def_local local symbol 'spam': def_local symbol table for function 'spam': local symbol 'a': def_param global_implicit symbol 'glob': use global_explicit symbol 'bar': def_global|def_local

若模块表为 module,块头格式是symbol table for module from file 'xxx':(stdin 时显示'<stdin>');非模块块则形如symbol table for [nested ]function 'spam':。逐层递归打印子表时用四个空格递增缩进。命令行内部等价于对每个文件执行一次symtable(src, filename, 'exec')


9. 综合实战:编写自己的作用域分析脚本

把前文各 API 串起来,即可写出一个可复制的、递归遍历符号表的分析工具。下面的示例针对一个典型“混合”源码,逐表打印名称、类型、行号、符号与子表:

import symtable SRC = '''\ import os T = 42 class Mine: instance_var = 24 def method(self, p): return T + p def outer(a, *args): x = 1 def inner(): return x + a return inner ''' def dump(table, level=0): pad = " " * level print(f"{pad}table {table.get_name()!r} | {table.get_type()!r} " f"| line {table.get_lineno()} | optimized={table.is_optimized()}" f" | nested={table.is_nested()}") for sym in table.get_symbols(): marks = [] for check in (("global", sym.is_global), ("local", sym.is_local), ("param", sym.is_parameter), ("free", sym.is_free), ("cell", sym.is_cell), ("imported", sym.is_imported), ("annotated", sym.is_annotated), ("decl-global", sym.is_declared_global), ("type-param", sym.is_type_parameter), ("namespace", sym.is_namespace)): if check[1](): marks.append(check[0]) print(f"{pad} {sym.get_name()!r}: {', '.join(marks)}") for child in table.get_children(): dump(child, level + 1) dump(symtable.symtable(SRC, "sample.py", "exec"))

在此基础上可演化出真实用途:

  • 作用域诊断/教学:回答“这个变量为什么在我的 lambda 里读不到”之类的经典问题(结合第 6.4 节的类作用域规则);
  • 静态分析工具的预处理层:在不执行代码的前提下判别名字的绑定角色(全局/局部/闭包/导入/类型参数),供 linter、重构工具或代码高亮使用;
  • 编译研究:对照Symbol的标志位与 Include/internal/pycore_symtable.h 的定义,反推compile()产物(如co_varnamesco_cellvarsco_freevars)的形成过程。

需要谨记的前提是:符号表分析只关心静态作用域与名字绑定关系,不执行任何代码,因此无法回答“变量运行时的值是多少”这类动态问题。


10. 版本能力速查表

综合 Doc/library/symtable.rst 的标注,symtable的关键能力演进如下,方便读者根据所用 Python 版本择用 API:

版本新增/变更
3.6Symbol.is_annotated()
3.12get_type()新增'annotation''TypeVar bound''type alias''type parameter'返回值
3.13SymbolTableType枚举取代裸字符串返回值;模块可作为脚本运行(python -m symtable
3.14Symbol.is_type_parameter()is_comp_iter()is_comp_cell()is_free_class()
3.15symtable()新增module参数;Function.get_cells()Symbol.is_cell()
next(开发分支)symtable()code参数支持 AST 对象

相关源码与测试索引:接口实现 · 命令行实现 · C 扩展模块_symtable· 符号表引擎 · C 端常量与作用域位编码 · 单元测试。官方文档还提示,若要在模块过滤下精确获得语法警告,请结合 Doc/library/warnings.rst 中的警告过滤器机制与module参数共同使用。

【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询