Python中import语句是模块导入的核心语法,其背后对应着一套完整的模块加载与执行流程,理解这个过程能帮助开发者排查模块导入相关的各类问题。

import语句执行的整体流程
当Python解释器遇到import语句时,整体会按照查找、加载、执行、缓存、绑定五个步骤完成模块导入,具体流程如下:
- 第一步:检查
sys.modules缓存中是否已有目标模块,若已存在则直接复用,不再重复加载执行 - 第二步:若缓存中不存在,按照模块搜索路径查找对应的模块文件
- 第三步:找到模块文件后,将其编译为字节码(如果是第一次导入),然后执行模块中的顶层代码
- 第四步:将执行后的模块对象存入
sys.modules缓存 - 第五步:将模块对象绑定到当前作用域的对应命名上
模块搜索路径规则
Python查找模块时,会按照固定顺序遍历搜索路径,搜索路径可以通过sys.path查看,默认顺序为:
- 当前执行脚本所在的目录
- 环境变量
PYTHONPATH中配置的目录 - Python安装目录下的标准库目录
- 第三方库的安装目录(如site-packages)
我们可以通过以下代码查看当前的模块搜索路径:
import sys
# 打印所有模块搜索路径
for path in sys.path:
print(path)
不同import语法的执行差异
常见的import语法有几种形式,它们的执行流程在绑定命名环节存在差异:
1. import 模块名
这种形式会将整个模块对象绑定到当前作用域,使用时需要通过模块名前缀访问内部属性:
# 导入math模块 import math # 使用模块内的sqrt函数 print(math.sqrt(4)) # 输出2.0
2. from 模块名 import 属性名
这种形式会将模块内的指定属性直接绑定到当前作用域,不需要加模块名前缀:
# 从math模块导入sqrt函数 from math import sqrt # 直接使用sqrt函数 print(sqrt(4)) # 输出2.0
3. from 模块名 import *
这种形式会将模块内所有未被__all__排除的属性绑定到当前作用域,若模块定义了__all__则只导入列表内的属性:
# 定义测试模块test_module.py
__all__ = ['func_a']
def func_a():
print("这是func_a")
def func_b():
print("这是func_b")
# 另一个文件中导入
from test_module import *
func_a() # 正常执行
func_b() # 报错,func_b未被导入
模块缓存机制验证
由于sys.modules的缓存作用,同一个模块在单次程序运行中只会被执行一次,我们可以通过以下示例验证:
首先创建测试模块test_cache.py,内容如下:
# test_cache.py内容
print("test_cache模块被执行")
count = 0
def add():
global count
count += 1
return count
然后在主程序中多次导入该模块:
import test_cache import test_cache # 第二次导入,不会重复执行模块代码 import test_cache # 第三次导入,同样不会重复执行 # 调用模块内的函数 print(test_cache.add()) # 输出1 print(test_cache.add()) # 输出2
运行后会发现test_cache模块被执行只打印了一次,说明后续导入直接复用了缓存中的模块对象,没有重复执行模块顶层代码。
循环导入的问题与原理
如果两个模块互相导入,就会形成循环导入,此时可能会触发属性未定义的错误,本质原因是模块加载流程未完成就被另一个模块引用:
比如模块a.py内容如下:
# a.py
import b
def func_a():
return "这是func_a"
print(b.func_b())
模块b.py内容如下:
# b.py
import a
def func_b():
return a.func_a()
运行a.py时会报错,因为导入b时b开始加载,加载过程中又导入a,此时a还未完成加载,func_a还未定义,就会触发异常。解决循环导入的常见方式是延迟导入,将import语句放到函数内部,或者在模块顶层只导入模块名,不直接使用模块内的属性。
Pythonimport执行流程模块加载sys_module修改时间:2026-07-17 12:21:32