仅从my_module导入find_index函数,为何仍输出模块顶层的'Module Imported...'
关于Python导入时顶层代码自动执行的问题解答
核心原理
from 模块名 import 指定对象从来不是“从模块文件中单独抽取指定函数的代码、跳过其他所有内容”的操作。
Python执行任何形式的模块导入时,第一步都会把目标模块完整加载,从上到下逐行执行所有顶层代码,等模块自身的变量、函数等内容全部加载到内存的命名空间里、模块初始化完成后,才会从已经准备好的模块对象中,取出你指定要导入的内容,挂载到当前运行文件的全局作用域中。
对应到示例的实际执行流程
当intro.py运行到from my_module import find_index这行代码时,Python的执行顺序和直觉里的“只拿find_index”完全不同:
- 识别到需要引入my_module模块,立刻打开my_module.py文件,从第一行开始逐行跑顶层代码
- 第一行没有任何缩进的
print('Module Imported...')属于纯顶层语句,模块加载时就会直接执行,根本不会判断你后续到底要用到模块里的哪些内容,只要导入模块就会触发 - 跑完打印逻辑,接着执行顶层变量赋值:把字符串
'Test String'绑定给test变量 - 再完成find_index函数的定义,把函数对象加载到my_module的内存空间里
- 第一行没有任何缩进的
- 等my_module所有顶层代码全部跑完、模块初始化完毕,Python才会从my_module的内存空间里找到find_index函数,把这个函数绑定到intro.py的全局作用域里,供后续代码调用
- 之后才轮到intro.py自己的逻辑执行:定义courses列表、调用find_index找到"History"的索引、打印计算结果
常见误区与优化方案
很多开发者一开始会以为Python的导入是“按需拿取用到的内容、不会跑无关代码”,实际上这种全量加载的设计是有现实意义的:很多模块的顶层代码本身就是用来做初始化的——比如加载依赖配置、初始化数据库连接、设置全局参数,这些逻辑本来就需要在模块被引入时就跑完,不管你后续调用模块里的哪个功能。
如果有些逻辑是不想在导入阶段执行的(比如调试打印、模块自测代码),常规的写法是把这些逻辑放到if __name__ == "__main__":的代码块里:
# 修改后的my_module.py test = 'Test String' def find_index(to_search, target): '''Find the index of a value in a sequence''' for i, value in enumerate(to_search): if value == target: return i return 'none' if __name__ == "__main__": # 只有直接运行my_module.py这个文件时,代码块内的逻辑才会执行 print('Module Imported...')
调整后再从其他文件导入find_index时,那句调试打印就不会在导入阶段自动跑出来了。
补充提示:Python会对已经加载过的模块做内存缓存,同一个程序运行过程中,哪怕重复导入好几次同一个模块,也只会在第一次导入时跑一遍所有顶层初始化代码,后续的重复导入不会再触发顶层逻辑。
内容的提问来源于stack exchange,提问作者Pedro Picciani
相关产品推荐
相关产品推荐

