You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python内置compile函数与多模块编译机制解析

多模块Python代码的编译与导入机制解析

问题背景

我正在学习Python的compile()函数与dis模块,单模块代码里能查看每行的编译过程,但想搞懂多模块的编译机制,于是准备了两个文件:

test.py

import sys
from temp import doubler

def tripler(n: int) -> int:
    f = 3 * n
        
    return f

if __name__ == '__main__':
    n = int(sys.argv[1]) if len(sys.argv) > 1 else 4
    t = tripler(n)
    s = doubler(t)
    print(s)

temp.py

def doubler(x):
    y = 2 * x
    return y

对test.py文本执行compile()后用dis.dis()得到的输出:

0           0 RESUME                   0

  1           2 LOAD_CONST               0 (0)
              4 LOAD_CONST               1 (None)
              6 IMPORT_NAME              0 (sys)
              8 STORE_NAME               0 (sys)

  2          10 LOAD_CONST               0 (0)
             12 LOAD_CONST               2 (('doubler',))
             14 IMPORT_NAME              1 (temp)
             16 IMPORT_FROM              2 (doubler)
             18 STORE_NAME               2 (doubler)
             20 POP_TOP

  4          22 LOAD_CONST               3 ('n')
             24 LOAD_NAME                3 (int)
             26 LOAD_CONST               4 ('return')
             28 LOAD_NAME                3 (int)
             30 BUILD_TUPLE              4
             32 LOAD_CONST               5 (<code object tripler at 0x00000255AB98F910, file "test.py", line 4>)
             34 MAKE_FUNCTION            4 (annotations)
             36 STORE_NAME               4 (tripler)

  9          38 LOAD_NAME                5 (__name__)
             40 LOAD_CONST               6 ('__main__')
             42 COMPARE_OP              40 (==)
             46 POP_JUMP_IF_FALSE       69 (to 186)

 10          48 PUSH_NULL
             50 LOAD_NAME                6 (len)
             52 LOAD_NAME                0 (sys)
             54 LOAD_ATTR               14 (argv)
             74 CALL                     1
             82 LOAD_CONST               7 (1)
             84 COMPARE_OP              68 (>)
             88 POP_JUMP_IF_FALSE       21 (to 132)
             90 PUSH_NULL
             92 LOAD_NAME                3 (int)
             94 LOAD_NAME                0 (sys)
             96 LOAD_ATTR               14 (argv)
            116 LOAD_CONST               7 (1)
            118 BINARY_SUBSCR
            122 CALL                     1
            130 JUMP_FORWARD             1 (to 134)
        >>  132 LOAD_CONST               8 (10)
        >>  134 STORE_NAME               8 (n)

 11         136 PUSH_NULL
            138 LOAD_NAME                4 (tripler)
            140 LOAD_NAME                8 (n)
            142 CALL                     1
            150 STORE_NAME               9 (t)

 12         152 PUSH_NULL
            154 LOAD_NAME                2 (doubler)
            156 LOAD_NAME                9 (t)
            158 CALL                     1
            166 STORE_NAME              10 (s)

 13         168 PUSH_NULL
            170 LOAD_NAME               11 (print)
            172 LOAD_NAME               10 (s)
            174 CALL                     1
            182 POP_TOP
            184 RETURN_CONST             1 (None)

  9     >>  186 RETURN_CONST             1 (None)

Disassembly of <code object tripler at 0x00000255AB98F910, file "test.py", line 4>:
  4           0 RESUME                   0

  5           2 LOAD_CONST               1 (3)
              4 LOAD_FAST                0 (n)
              6 BINARY_OP                5 (*)
             10 STORE_FAST               1 (f)

  7          12 LOAD_FAST                1 (f)
             14 RETURN_VALUE

我能看到从temp模块导入了doubler符号,但看不到它对应的code object,也不清楚它是在哪个阶段被编译的,而代码对象能正常执行。请问有人能解释temp模块的处理过程吗?


解答

1. compile()仅处理当前模块,不触发依赖编译

你调用compile()处理test.py文本时,这个操作只会编译当前模块的代码,不会主动去读取、编译依赖的temp.py。test.py里的from temp import doubler在编译阶段只会被转换成IMPORT_NAME、IMPORT_FROM这类字节码指令,此时Python还不会处理temp模块的内容。

2. temp模块的编译与加载发生在运行时

当test.py的字节码运行到IMPORT_NAME temp指令时,Python才会启动temp模块的加载流程:

  • 先检查sys.modules,确认temp模块是否已被加载;
  • 若未加载,找到temp.py文件并读取内容,调用compile()将其编译为字节码(生成对应的code object);
  • 执行temp模块的字节码,定义doubler函数(此时doubler的code object会被创建并关联到函数对象上);
  • 通过IMPORT_FROM指令把doubler函数对象导入到test模块的命名空间中。

3. 看不到doubler的code object的原因

你是直接对test.py编译后的字节码反汇编,而temp模块的code object是在运行时才生成的,不会包含在test模块的编译结果里。如果想要查看doubler的字节码,可以在运行时添加代码:

import dis
dis.dis(doubler)

或者直接对temp.py单独执行compile()和dis.dis()。

4. 模块编译的缓存机制

Python会把编译后的字节码缓存到__pycache__目录下的.pyc文件中,下次导入temp模块时,若temp.py未修改,会直接加载缓存的字节码,跳过重新编译步骤。


内容的提问来源于stack exchange,提问作者ACBlue

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 15:13:13