Python 模块、包与虚拟环境

AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。

Python 3.13

代码一多就要拆分文件,依赖一多就要隔离环境。这一篇讲模块、包与虚拟环境的标准做法。

环境准备

示例在 Python 3.13 下验证,venv 与 pip 随 Python 一起安装。

python -m pip --version

模块就是 .py 文件

utils.py:

def slugify(text: str) -> str:
    return "-".join(text.lower().split())

同目录下导入:

import utils                 # 用 utils.slugify(...)
from utils import slugify    # 直接用 slugify(...)
import numpy as np           # as 起别名(第三方库约定别名)

推荐 import module 而不是 from module import *——后者污染命名空间,看不出名字从哪来。

导入是怎么找模块的

按顺序搜索:

  1. 已加载模块(sys.modules)
  2. 脚本所在目录
  3. 环境变量 PYTHONPATH
  4. 标准库与 site-packages
import sys
print(sys.path)      # 搜索路径列表

导入时执行的字节码会缓存到 __pycache__/,无需提交到版本库。

包

目录里放 __init__.py 就是包(Python 3 也支持无 __init__.py 的命名空间包,但显式写更清楚):

myapp/
├── __init__.py
├── cli.py
└── utils.py
from myapp import utils        # 绝对导入,推荐
from myapp.utils import slugify

包内部互相引用可用相对导入(一个点表示当前包,两个点表示上级):

# myapp/cli.py
from .utils import slugify
from ..config import load

相对导入只在包内有效,直接 python myapp/cli.py 运行会报 ImportError: attempted relative import。要这样运行:

python -m myapp.cli

__name__ == "__main__"

# myapp/cli.py
def main():
    print("run")

if __name__ == "__main__":
    main()

作用:直接运行时执行 main();被别人 import 时不执行。测试、复用都靠它。

虚拟环境

每个项目独立一套依赖,避免版本打架:

python -m venv .venv

# 激活
source .venv/bin/activate        # macOS / Linux
.venv\Scripts\activate           # Windows

# 退出
deactivate

激活后 python 与 pip 指向虚拟环境内的版本:

which python        # 应指向项目下的 .venv
pip install requests

不激活也能用(CI 常用):

.venv/bin/python -m pip install requests
.venv/bin/python myapp/cli.py

依赖管理

安装与固化:

pip install requests==2.32.3      # 指定版本
pip install -r requirements.txt   # 按文件安装
pip freeze > requirements.txt     # 导出当前环境的确切版本
pip list                          # 查看已安装
pip show requests                 # 查看详情

requirements.txt 示例:

requests==2.32.3
rich==13.9.4

写死版本(==)能保证别人装到和你一样的版本,避免「我这能跑」问题。

现代项目用 pyproject.toml 声明元数据与依赖:

[project]
name = "myapp"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = ["requests>=2.32"]

[project.scripts]
myapp = "myapp.cli:main"

开发安装:

python -m pip install -e .      # 可编辑安装,改代码立即生效

.gitignore 至少包含:

.venv/
__pycache__/
*.pyc

常见错误与注意事项

1. 装到全局环境

忘了激活虚拟环境,pip install 会装到系统 Python,可能污染系统包并需要管理员权限。

2. 脚本名与标准库同名

文件叫 json.py / email.py / random.py,会把自己导入进来,报奇怪的 AttributeError。改名即可。

3. 循环导入

a.py 导入 b,b 又导入 a。解法:把共用部分抽到第三个模块,或把导入移到函数内部(延迟导入)。

4. 相对导入在脚本模式下失败

用 python -m 包名.模块 运行,或改用绝对导入。

5. 提交 .venv 到 Git

虚拟环境包含绝对路径,换机器不可用。只提交 requirements.txt / pyproject.toml。

小结

  • 一个 .py 是一个模块,含 __init__.py 的目录是包。
  • 优先绝对导入;包内可用相对导入,用 python -m 运行。
  • if __name__ == "__main__": 区分「运行」与「被导入」。
  • 每项目一个 venv,依赖写进 requirements.txt 或 pyproject.toml 并锁版本。

下一步