Python 模块系统全景:
sys.path决定 import 去哪找模块,四种方案解决跨项目导入,__main__与-m决定模块怎么运行。
模块与 sys.path
**模块(module)**就是一个 .py 文件,**包(package)**是包含 __init__.py 的目录。import 一个模块时,Python 按 sys.path 列表从前往后查找:
import sys
print(sys.path)
# ['', '/usr/lib/python3.10', ..., 'site-packages']'':脚本所在目录(交互模式下是当前工作目录)- 标准库路径
- 第三方库路径(site-packages)
绝大多数 import 报错的根源都是同一个:目标模块不在 sys.path 里。后面的跨项目导入、相对导入失败,都是这句话的不同表现形式。
import 查找顺序
sys.path 从前往后扫描,第一个命中的模块生效。当前目录下若有 random.py,会遮蔽标准库的 random——不要用标准库已有的名字命名自己的模块。确认模块实际来自哪里:
import random
print(random.__file__)跨项目 import:四种方案
多个项目互相导入(比如把 models/Practical-RIFE 的代码 import 进当前项目),本质都是让对方的目录进入 sys.path。四种方案对比:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
修改 sys.path | 简单直接 | 需在每个入口添加代码 | 快速调试、脚本开发 |
设置 PYTHONPATH | 一次设置全局生效 | 依赖系统配置 | 长期开发 |
| 优化项目结构 | 结构规范、依赖明确 | 需创建 setup.py | 长期维护项目 |
使用 importlib | 灵活、避免全局污染 | 语法复杂 | 动态加载场景 |
修改 sys.path
import sys, os
project_path = os.path.join(os.path.dirname(__file__), "models", "Practical-RIFE")
sys.path.append(project_path)
from video_interpolation_operator import VideoInterpolationOperator设置 PYTHONPATH
Linux/MacOS:
export PYTHONPATH=/path/to/models/Practical-RIFE:/path/to/models/AMT:$PYTHONPATHWindows:
set PYTHONPATH=C:\path\models\Practical-RIFE;C:\path\models\AMT;%PYTHONPATH%优化项目结构(推荐)
创建 setup.py:
from setuptools import setup, find_packages
setup(name="practical-rife", version="0.1.0", packages=find_packages())安装:
pip install -e .使用 importlib
import importlib.util, os
spec = importlib.util.spec_from_file_location("module", "/path/to/module.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)模块运行:__main__ 与 -m
同一个模块有两种启动方式,差别就在 sys.path 的起点和包上下文上。
核心原理
python -m <模块名> 将指定模块以脚本方式运行:
- 按
sys.path搜索模块 - 将当前工作目录加入
sys.path前端 - 设置
__name__ = "__main__"
直接运行 python script.py 时,加入 sys.path 前端的则是脚本所在目录。
-m vs 直接运行
| 方式 | 命令 | sys.path 起点 | 相对导入 |
|---|---|---|---|
-m | python -m module | 当前工作目录 | ✅ 正常 |
| 直接运行 | python script.py | 脚本所在目录 | ❌ 可能失败 |
典型用法
# 运行标准库模块
python -m http.server 8000
python -m pip install --upgrade pip
# 运行调试器
python -m debugpy --listen 5678 script.py
# 运行自定义包(包内需要 __main__.py)
python -m my_package相对导入为什么只有 -m 能跑通
my_project/
├── main.py
└── utils/
└── helper.py# main.py
from .utils.helper import func # 相对导入# ❌ 直接运行失败
python main.py # ImportError
# ✅ -m 运行成功
cd .. && python -m my_project.main原因在包上下文:相对导入的锚点是「当前模块属于哪个包」。直接运行 main.py 时,它是顶层脚本,没有所属包,相对导入的 . 无从解析,抛出 ImportError;-m 则先把模块按包成员身份正常导入(包上下文完整),再执行其代码,相对导入自然成立。cd .. 保证当前工作目录——也就是 -m 的 sys.path 起点——能看到 my_project 这个包。
通用建议
- 使用虚拟环境隔离依赖
- 确保每个子项目包含
__init__.py - 避免循环导入