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_FAST、LOAD_GLOBAL、LOAD_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,且TypeAliasBlock、TypeParametersBlock、TypeVariableBlock等新型块在 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 的实现可以发现两个值得注意的机制:
- 惰性缓存:
SymbolTable.lookup()第一次查询某名字时,才去底层读取该符号的标志位并构造Symbol对象,随后缓存在self._symbols字典中;get_symbols()是对get_identifiers()逐个lookup()的结果。 - 命名空间自动挂接:
lookup()内部还会调用__check_children(),把子表中与该符号同名的那些SymbolTable收集起来挂到符号对象上——这正是后面Symbol.is_namespace()/get_namespaces()的数据来源:一个名字(如def namespace_test)若被多次def,就会关联到多个命名空间。
顶层表对象本身由工厂SymbolTableFactory借助WeakValueDictionary做内存复用,保证同一底层表在多次访问时返回相同的 Python 包装对象(见 Lib/symtable.py)。
5. Function 与 Class:针对函数、类的特化接口
Function和Class都继承自SymbolTable(定义于 Lib/symtable.py),由工厂按底层 block 类型自动选择构造:
- 底层类型为函数 →
Function - 底层类型为类 →
Class - 其余一律是普通
SymbolTable
Class目前没有额外方法;Function则基于标识符的标志位过滤,给出六类“角色名单”,全部返回元组,且结果会被缓存(首次计算后保存到实例属性,见 Lib/symtable.py):
| 方法 | 返回内容 | 底层过滤依据 |
|---|---|---|
get_parameters() | 形参名元组 | 标志含DEF_PARAM |
get_locals() | 局部变量名元组 | scope 为LOCAL或CELL |
get_globals() | 全局名元组 | scope 为GLOBAL_IMPLICIT或GLOBAL_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声明后赋值的bar、some_assigned_global_var是“显式全局”; - 内部函数
internal引用了外层spam的x,于是对internal而言x是 free 变量(FREE),对spam而言x、some_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模块导出这些常量:USE、DEF_GLOBAL、DEF_NONLOCAL、DEF_LOCAL、DEF_PARAM、DEF_TYPE_PARAM、DEF_FREE_CLASS、DEF_IMPORT、DEF_BOUND、DEF_ANNOT、DEF_COMP_ITER、DEF_COMP_CELL、SCOPE_OFF、SCOPE_MASK,以及五种作用域FREE、LOCAL、GLOBAL_IMPLICIT、GLOBAL_EXPLICIT、CELL(见 Lib/symtable.py)。
Symbol的repr会同时展示作用域与标志位,便于调试:
<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,该名字也可能同时绑定到不引入新命名空间的普通值(如int、list)。
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中符号T的repr为:
<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_varnames、co_cellvars、co_freevars)的形成过程。
需要谨记的前提是:符号表分析只关心静态作用域与名字绑定关系,不执行任何代码,因此无法回答“变量运行时的值是多少”这类动态问题。
10. 版本能力速查表
综合 Doc/library/symtable.rst 的标注,symtable的关键能力演进如下,方便读者根据所用 Python 版本择用 API:
| 版本 | 新增/变更 |
|---|---|
| 3.6 | Symbol.is_annotated() |
| 3.12 | get_type()新增'annotation'、'TypeVar bound'、'type alias'、'type parameter'返回值 |
| 3.13 | SymbolTableType枚举取代裸字符串返回值;模块可作为脚本运行(python -m symtable) |
| 3.14 | Symbol.is_type_parameter()、is_comp_iter()、is_comp_cell()、is_free_class() |
| 3.15 | symtable()新增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),仅供参考