Python 模块、包与虚拟环境
AI 辅助生成本文由 AI 起草并经人工核查,示例均标明软件版本;发现错误请以官方文档为准。
代码一多就要拆分文件,依赖一多就要隔离环境。这一篇讲模块、包与虚拟环境的标准做法。
环境准备
示例在 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 *——后者污染命名空间,看不出名字从哪来。
导入是怎么找模块的
按顺序搜索:
- 已加载模块(
sys.modules) - 脚本所在目录
- 环境变量
PYTHONPATH - 标准库与
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并锁版本。