基础

Python 模块与包:import、pip、venv 与库,通俗讲解

更新于: · Skillaria.Top

模块是 Python 中的单个 .py 文件,包是包含模块和 __init__.py 的文件夹,库则是为某项任务准备的一组包。第三方库通过 pip(或 uv)安装到虚拟环境 venv 中——这是一个独立的文件夹,包含解释器和该项目的包,以免不同项目的版本互相冲突。

Python 中的模块就是普通的代码文件,import 则是从模块中取出函数和类的方式。模块组成包,包组成库;为了让不同项目的第三方库互不干扰,应把它们安装到虚拟环境 venv 中。下面梳理整个链条:导入时发生了什么,包的结构如何,pip 和 venv 如何工作,以及 Python 3.15 中新出现的惰性导入。

模块、包、库:有什么区别

概念是什么示例
模块一个 .py 文件;模块名就是去掉扩展名的文件名categories.py → import categories
包包含模块和 __init__.py 文件的文件夹expenses/report.py → from expenses.report import build
库为某项任务准备好的一组模块和包requests、pandas、标准库中的 pathlib
发行包(PyPI 上的包)pip 安装的压缩包pip install requests

在日常交流中,“库”“包”和“模块”经常被混用,这很正常。关键是要明白:从技术上讲,import 总是加载一个模块或包,而 pip 安装的是 PyPI 上的发行包,发行包内部含有各种包。

import 在 Python 中如何工作

导入有几种形式,实际代码中都会遇到:

import math                    # 整个模块,通过 math.sqrt 访问
from math import sqrt, pi      # 指定的名字
import datetime as dt          # 别名
from collections import Counter as C

print(math.sqrt(16), sqrt(9), pi)
print(dt.date.today())

对于大型库,常用别名:import numpy as np、import pandas as pd。不建议使用 from module import *:它会污染命名空间,并且看不出名字来自哪里。

import 实际上做了什么

  1. 在缓存 sys.modules 中查找模块。如果已经加载,直接返回。
  2. 如果没有加载,就按 sys.path 中的目录查找文件:运行脚本所在的文件夹、标准库、虚拟环境的 site-packages。
  3. 从上到下完整执行找到的文件。
  4. 把模块对象放入缓存,并绑定到名字上。

由此得出两个实用结论:再次导入同一模块几乎不耗时——代码不会第二次执行;而模块顶层的所有代码,包括 print 和耗时的计算,都会在导入时执行。

import sys
for p in sys.path:
    print(p)
print("math" in sys.modules)

if __name__ == "__main__"

既然导入会执行文件,就需要一种方法区分“我被当作脚本运行”和“我被导入了”。为此可以使用变量 __name__:运行文件时它等于 "__main__",导入时则等于模块名。

# categories.py
def guess(title: str) -> str:
    return "餐饮" if "咖啡" in title.lower() else "其他"

if __name__ == "__main__":
    print(guess("家附近的咖啡馆"))   # 只有在执行 python categories.py 时才运行

对于包,还可以在包内放一个 __main__.py 文件,然后通过 python -m expenses 运行。

Python 标准模块

Python 自带“电池”:数百个内置模块无需安装即可使用。最常用的有:

模块用途
os、sys、pathlib、shutil系统、路径、文件
json、csv、pickle数据格式
datetime、time、calendar日期与时间
math、random、statistics、fractions数字
re、string文本处理
collections、itertools、functools数据结构与迭代器
logging、argparse、subprocess日志、命令行参数、运行程序
asyncio、threading异步与线程

标准库的全部模块名保存在 sys.stdlib_module_names 中(Python 3.10+)。查看模块的内容可以使用 dir(math) 和 help(math)。

自己的包:项目结构

当模块变多时,把它们组织成包。常见的布局是 src-layout:

expenses-project/
├── pyproject.toml
└── src/
    └── expenses/
        ├── __init__.py
        ├── cli.py
        ├── parsing.py
        ├── categories.py
        └── report.py

包内的模块可以通过包名互相导入(from expenses.categories import guess),也可以使用相对导入(from .categories import guess)。相对导入只在包内有效,对直接作为脚本运行的文件无效。

pip:安装 Python 库

第三方库存放在 PyPI 目录中,由随 Python 一起提供的 pip 工具安装。最可靠的方法是通过解释器调用 pip——这样库一定会安装到运行代码所用的那个 Python 中。

python -m pip install requests          # 安装
python -m pip install "pandas>=2.3"     # 指定版本范围
python -m pip install --upgrade requests
python -m pip uninstall requests        # 卸载
python -m pip list                      # 已安装包列表
python -m pip show requests             # 版本和依赖
python -m pip freeze > requirements.txt
python -m pip install -r requirements.txt

在 Windows 上,常用启动器 py 代替 python。如果找不到 pip,可以用内置模块安装它:python -m ensurepip --upgrade。

常用的 Python 库

PyPI 上的第三方包有数十万个,但大多数项目用到的只是下面这一小部分。从它们开始,而不是去找冷门的工具。

任务库
HTTP 请求与 APIrequests、httpx
数据分析与表格pandas、numpy
图表matplotlib
Web 服务fastapi、django、flask
测试pytest
图像pillow(导入时名为 PIL)

注意:安装时的名字和导入时的名字并不总是相同。安装 pillow 时使用 pip install pillow,导入时写 from PIL import Image;安装 opencv-python,导入的是 cv2。如果安装后 import 立即失败,请查阅该包的文档,确认导入名。

Linux 上安装库

在新版 Debian 和 Ubuntu 中,向系统 Python 执行 pip install 会以 externally-managed-environment 错误结束(PEP 668)。这是一种保护:系统 Python 属于操作系统的包管理器,pip 不应破坏它。正确的解决办法不是 --break-system-packages,而是使用虚拟环境。

venv:Python 虚拟环境

虚拟环境是一个独立的文件夹,里面有自己的解释器(指向原解释器的链接)和独立的 site-packages。在其中安装的库只对该项目可见。因此,一个项目可以使用 Django 4,另一个项目可以使用 Django 5,二者互不冲突。

模块 venv 是 Python 自带的,无需单独安装(在 Debian/Ubuntu 上有时需要系统包 python3-venv)。

# 在 .venv 文件夹中创建虚拟环境
python -m venv .venv

# 激活:Linux 和 macOS
source .venv/bin/activate
# 激活:Windows PowerShell
.venv\Scripts\Activate.ps1

# 现在 pip 会把包安装到 .venv 中
python -m pip install requests
python -c "import sys; print(sys.prefix)"

# 退出虚拟环境
deactivate

激活后,终端行首会出现 (.venv)。虚拟环境文件夹不应提交到 git(把 .venv/ 加入 .gitignore),也不应在不同机器之间复制——应该保存依赖列表,并在需要时重新创建环境。如果 PowerShell 阻止运行激活脚本,可以为当前用户允许执行:Set-ExecutionPolicy -Scope CurrentUser RemoteSigned。

uv 与 pyproject.toml

现代项目将依赖写在 pyproject.toml 中。课程中使用的 uv 工具把创建环境和安装合并在一起:uv add pandas 会下载包,把它写入 pyproject.toml,并把精确版本固定在 uv.lock 中。同事克隆项目后执行 uv sync,就能得到完全一致的环境。

[project]
name = "expenses"
version = "0.1.0"
requires-python = ">=3.15"
dependencies = [
    "pandas>=2.3",
]

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

Python 3.15 中的惰性导入(PEP 810)

本主题在 3.15 中的主要新功能是关键字 lazy。大型库的加载很慢:课程的测试中,在 python:3.15-slim 镜像上,普通的 import pandas 耗时约 415 毫秒,而 lazy import pandas 只需约 13 毫秒。对于那些只为了显示 --help 而运行的命令行工具,差别非常大。

lazy import pandas as pd
lazy from expenses.report import build_report

def main() -> None:
    ...
    df = pd.read_csv(path)   # 这里才真正加载 pandas

此时名字绑定的是一个代理对象(types.LazyImportType),真正的导入发生在第一次使用该名字时。如果一直没有用到,就完全不会耗费时间。

惰性导入的限制

  • 只能用于模块的顶层:放在函数、类或 try/except 中会产生 SyntaxError。
  • 不允许 lazy from x import *。
  • ModuleNotFoundError 不会在启动时出现,而是在第一次使用时才抛出。
  • 模块的副作用(例如注册插件)会推迟到之后执行。

要让所有导入都变成惰性,可以使用参数 -X lazy_imports=all 或环境变量 PYTHON_LAZY_IMPORTS=all;例外情况通过 sys.set_lazy_imports_filter() 设置。对于还需要兼容旧版本的代码,可以使用模块变量 __lazy_modules__ = ["pandas"]:3.15 会把列出的导入变成惰性,而旧版本只会执行普通的 import。想知道启动时间花在了哪里,可以运行 python -X importtime -c "import pandas"。收益主要体现在频繁的短时启动程序上;对长期运行的服务器,惰性导入几乎没有帮助。

常见错误

  • ModuleNotFoundError: No module named 'requests' — 库安装在了另一个 Python 中,或者没有激活虚拟环境。用同一个解释器执行 python -m pip show requests 检查。
  • 自己的文件被命名为 json.py、random.py 或 csv.py — 脚本所在的文件夹位于 sys.path 的最前面,它会覆盖标准模块。Python 3.13+ 的错误信息会提示“consider renaming”。请重命名文件,并删除 __pycache__。
  • 循环导入:a.py 导入 b.py,而 b.py 又导入 a.py。把公共部分移到第三个模块中,或者在函数内部导入。
  • 在 Python 解释器中输入 pip(>>> pip install)会得到 SyntaxError。pip 命令应该在终端中执行。

小结

模块是文件,包是模块的文件夹,库是它们的成套集合;import 先在缓存和 sys.path 中查找模块,并且只执行一次。第三方库应当只通过 pip 或 uv 安装到 venv 虚拟环境中——这样项目之间互不干扰,环境也容易复现。课程中这一主题同样结合实践讲解:支出记录器被整理成一个带依赖的包,并借助惰性导入加快启动速度。

常见问题

每个项目都需要单独创建 venv 吗?

是的,这是良好的习惯:每个项目可能需要不同版本的库,独立的环境可以避免冲突,也不会弄乱系统 Python。

pip 和 uv 有什么区别?

pip 只负责安装包;uv 还能创建环境、把依赖写入 pyproject.toml,并把精确版本固定在锁文件中,同时速度明显更快。

如何查看已安装的 Python 库列表?

在目标环境中执行 python -m pip list。pip freeze 会以 requirements.txt 的格式输出同样的列表。

已安装的模块保存在哪里?

保存在当前环境的 site-packages 文件夹中。要查看具体模块的路径,可以使用 __file__ 属性,例如 import requests; print(requests.__file__)。

能否把 venv 文件夹复制到另一台电脑上?

不能。虚拟环境与路径和本机的解释器绑定。应该复制依赖列表,然后在新机器上重新创建环境。

通过视频学习本主题

课程“Python 3.15: от новичка до профи”结合实践详细讲解了这一主题。

前往课程