ZetCode

Python compile 函数

上次修改时间:2025 年 4 月 11 日

这份全面的指南探讨了 Python 的 compile 函数,该函数将源代码转换为字节码或 AST 对象。我们将介绍语法模式、代码对象以及动态代码编译的实践示例。

基本定义

compile 函数将源代码转换为可以被 evalexec 执行的代码或 AST 对象。它支持三种模式:execevalsingle

关键参数:source(字符串/字节/AST)、filename(用于错误消息)、mode(exec/eval/single)、flags(优化)、dont_inherit(编译器选项)。返回一个准备好执行的代码对象。

基本编译示例

此示例演示了如何使用 compile 函数的不同模式编译一个简单的表达式和语句。

basic_compile.py
# Compile an expression for eval
code_obj = compile('3 + 4 * 2', '<string>', 'eval')
print(eval(code_obj))  # 11

# Compile a statement for exec
code_obj = compile('x = 5\ny = 10\nprint(x + y)', '<string>', 'exec')
exec(code_obj)  # 15

# Compile for single mode (interactive)
code_obj = compile('print("Hello")', '<string>', 'single')
exec(code_obj)  # Hello

第一个示例编译一个用于 eval 的表达式,它期望一个单一的表达式。第二个示例编译多个用于 exec 的语句。

single 模式用于交互式使用 - 它像 Python REPL 一样打印表达式结果。请注意每种模式的不同返回行为。

从文件编译

此示例演示了从文件编译 Python 代码,这对于实现自定义解释器或预处理代码非常有用。

file_compile.py
# Assume test.py contains: print("Hello from file")

with open('test.py', 'r') as f:
    source = f.read()

code_obj = compile(source, 'test.py', 'exec')
exec(code_obj)  # Hello from file

# Inspect the code object
print(f"Co_code: {code_obj.co_code[:20]}...")  # First 20 bytes
print(f"Co_names: {code_obj.co_names}")  # Used names

在这里,我们从文件读取 Python 代码并编译它。 filename 参数有助于显示错误消息。然后,我们执行已编译的代码对象。

该示例还显示了检查代码对象的属性,例如 co_code(字节码)和 co_names(使用的变量名)。这些对于内省非常有用。

AST 编译

这个高级示例演示了从抽象语法树 (AST) 编译,从而实现强大的代码生成和转换功能。

ast_compile.py
import ast

# Create AST for: print("Hello AST")
tree = ast.Module(
    body=[ast.Expr(
        value=ast.Call(
            func=ast.Name(id='print', ctx=ast.Load()),
            args=[ast.Constant(value='Hello AST')],
            keywords=[]
        )
    )],
    type_ignores=[]
)

# Compile and execute AST
code_obj = compile(tree, '<ast>', 'exec')
exec(code_obj)  # Hello AST

# Convert AST back to source
print(ast.unparse(tree))  # print('Hello AST')

我们手动构造一个表示 print 语句的 AST,然后编译并执行它。这演示了 Python 的完整编译管道。

ast.unparse 将 AST 转换回源代码,显示了往返转换能力。 AST 操作实现了强大的元编程。

错误处理

此示例显示了编译无效代码时如何正确处理错误,包括语法错误和编译警告。

compile_errors.py
# Syntax error
try:
    compile('print("Hello)', '<string>', 'exec')  # Missing quote
except SyntaxError as e:
    print(f"SyntaxError: {e}")

# TypeError for wrong mode
try:
    compile('x = 5', '<string>', 'eval')  # 'eval' needs expression
except SyntaxError as e:
    print(f"SyntaxError: {e}")

# Warning example
import warnings
warnings.simplefilter('always')
compile('from __future__ import braces', '<string>', 'exec')

第一个案例显示了处理语法错误(未关闭的字符串)。第二个案例演示了模式不匹配错误(在 eval 模式下赋值)。

最后一个示例触发了针对无效未来导入的 SyntaxWarning。可以通过 warnings 模块控制警告。

优化标志

此示例演示了使用编译标志来控制优化和未来功能行为。

flags_compile.py
from __future__ import annotations
import ast

# With optimizations (constant folding)
code_obj = compile('3 + 4 * 2', '<string>', 'eval', 
                  flags=ast.PyCF_ONLY_AST, optimize=2)
print(ast.dump(code_obj, indent=2))  # Shows optimized AST

# Without optimizations
code_obj = compile('3 + 4 * 2', '<string>', 'eval', optimize=0)
print(eval(code_obj))  # 11

# With future features
code_obj = compile('def foo(x: int) -> None: pass', 
                  '<string>', 'exec', 
                  flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT)

第一部分显示了使用优化标志生成 AST。第二部分比较了优化和未优化的编译。第三部分演示了未来功能。

优化级别 2 执行常量折叠。未来标志启用了诸如顶级 await 之类的功能。 这些标志提供了细粒度的编译控制。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程