浏览知识库目录

Python

模块、包、虚拟环境与依赖

理解导入系统和依赖方向,把单文件脚本整理为可安装、可测试的 Python 包。

模块、包、虚拟环境与依赖

当脚本开始承担多个职责时,继续向同一个文件追加函数会让导入、副作用和测试边界逐渐失控。本篇把任务管理器拆成真正的 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,其中一个模块可能在初始化完成前被访问。

解决方向:

  1. 将共享类型下沉到更基础的模块。
  2. 让依赖朝单一方向流动。
  3. 只为类型检查需要的导入放进 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,不要跨机器复制。


十、练习与自测

  1. 把任务规范化、筛选和显示拆到三个模块,并画出依赖方向。
  2. __init__.py 中只导出稳定公共接口。
  3. 写一个诊断命令打印包文件位置,确认导入来源。

自测:

  • 为什么模块顶层不应连接数据库?
  • 可编辑安装与 wheel 安装分别用于什么阶段?
  • 发行项目名和导入包名为何可以不同?

十一、官方资料

上一篇:控制流、函数与作用域 | 下一篇:类、协议与 Python 数据模型