Python
模块、包、虚拟环境与依赖
理解导入系统和依赖方向,把单文件脚本整理为可安装、可测试的 Python 包。
发布于 2026年7月23日
模块、包、虚拟环境与依赖
当脚本开始承担多个职责时,继续向同一个文件追加函数会让导入、副作用和测试边界逐渐失控。本篇把任务管理器拆成真正的 Python 包。
一、学习目标
- 理解模块、普通包和导入缓存
- 正确使用绝对导入与相对导入
- 建立
src/study_tasks目录结构 - 区分运行依赖与开发依赖
- 用
pyproject.toml声明项目元数据
二、模块与包
一个 .py 文件通常对应一个模块。包含 __init__.py 的目录是普通包:
src/study_tasks/
__init__.py
models.py
service.py
cli.py
导入:
from study_tasks.models import Task
from study_tasks.service import TaskService
包内部可以使用显式相对导入:
# src/study_tasks/service.py
from .models import Task
对项目外部展示的接口,集中从 __init__.py 导出:
from .models import Task, TaskStatus
from .service import TaskService
__all__ = ["Task", "TaskService", "TaskStatus"]
三、导入会执行模块顶层代码
print("模块正在导入")
第一次导入时,Python 创建模块对象并执行顶层语句,之后通常从 sys.modules 复用。顶层代码应以常量、类和函数定义为主,不要在导入时连接数据库、发网络请求或启动线程。
入口使用保护条件:
def main() -> int:
print("study_tasks")
return 0
if __name__ == "__main__":
raise SystemExit(main())
这样导入模块不会启动程序。
四、循环导入
假设 models.py 导入 service.py,同时 service.py 又导入 models.py,其中一个模块可能在初始化完成前被访问。
解决方向:
- 将共享类型下沉到更基础的模块。
- 让依赖朝单一方向流动。
- 只为类型检查需要的导入放进
TYPE_CHECKING。
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .service import TaskService
不要用函数内部导入长期掩盖错误的模块边界。
五、项目元数据
[build-system]
requires = ["hatchling>=1.27"]
build-backend = "hatchling.build"
[project]
name = "study-tasks"
version = "0.1.0"
description = "A small command-line task manager"
requires-python = ">=3.14"
dependencies = []
[project.optional-dependencies]
dev = [
"pytest>=8.3",
"ruff>=0.11",
"mypy>=1.15",
]
[tool.hatch.build.targets.wheel]
packages = ["src/study_tasks"]
运行依赖是最终用户运行程序必需的包;开发依赖只用于测试、检查和构建。标准库模块不写入 dependencies。
六、安装方式
开发环境使用可编辑安装:
python -m pip install -e ".[dev]"
可编辑安装让解释器直接读取当前源码,修改后无需重新安装。发布前必须另外构建 wheel,并在干净环境安装 wheel 验证,不能只依赖开发模式。
查看项目:
python -m pip show study-tasks
python -c "import study_tasks; print(study_tasks.__file__)"
七、模块入口
创建 src/study_tasks/__main__.py:
from .cli import main
raise SystemExit(main())
现在可以运行:
python -m study_tasks
之后会在 pyproject.toml 中添加控制台脚本,让用户运行 study-tasks。
八、依赖管理原则
- 优先确认标准库是否已经满足需求。
- 第三方依赖只在明确降低复杂度时引入。
- 检查维护状态、许可证、Python 版本支持和安全公告。
- 应用项目应保留可重建的锁定结果;可复用库通常声明兼容范围而不是锁死间接依赖。
- 安装和运行命令始终使用同一虚拟环境。
pip freeze 展示当前环境的全部已安装包,不等于经过设计的直接依赖清单。
九、常见错误
直接运行包内文件
python src/study_tasks/cli.py
显式相对导入可能失败,因为该文件没有以包成员身份运行。应安装项目后运行模块或控制台命令。
项目名与导入名混淆
发行项目名可以是 study-tasks,导入包名使用合法标识符 study_tasks。
把虚拟环境提交到版本库
.venv 是可重建产物,应加入 .gitignore,不要跨机器复制。
十、练习与自测
- 把任务规范化、筛选和显示拆到三个模块,并画出依赖方向。
- 在
__init__.py中只导出稳定公共接口。 - 写一个诊断命令打印包文件位置,确认导入来源。
自测:
- 为什么模块顶层不应连接数据库?
- 可编辑安装与 wheel 安装分别用于什么阶段?
- 发行项目名和导入包名为何可以不同?
十一、官方资料
上一篇:控制流、函数与作用域 | 下一篇:类、协议与 Python 数据模型