一个文件就是一个模块
当你的 Python 代码超出单个脚本的规模,你就会想把它拆分到多个文件里。Python 的模块系统很简单:任何 .py 文件都会自动成为一个模块,可以按名字导入。
假设你有一个文件 util.py:
# util.py
def greet(name):
return f"Hello, {name}"
PI = 3.14159
在同一目录下的另一个文件 main.py 中,你可以使用它:
# main.py
import util
print(util.greet("Ada"))
print(util.PI)
运行 python3 main.py,Python 会找到 util.py,运行一次,并把顶层定义的所有东西(函数、变量、类)作为 util 模块的属性提供出来。
三种导入方式
导入整个模块。通过模块名访问其中的内容:
导入指定的名字。它们可以直接使用:
用别名导入。当模块名很长,或者两个模块重名时很有用:
import numpy as np
import pandas as pd
# Now use np.array(...), pd.DataFrame(...), etc.
还有第四种写法 from math import *,它会把所有东西都引入你的命名空间。除了在 REPL 里做实验,请避免使用它:它让你无法分辨一个名字来自哪里,还会造成悄无声息的命名冲突。
导入时会发生什么
在一个程序中,模块第一次被导入时,Python 会执行一次它的顶层代码。之后再导入同一个模块,返回的是已经加载好的版本。所以这段代码:
# setup.py
print("setup module loading...")
VALUE = 42
# main.py
import setup
import setup # imported twice - but "loading" only prints once
print(setup.VALUE)
只会打印一次 "setup module loading..."。这一点很重要:如果你把副作用(比如打开文件或打印)放在模块顶层,它们会在导入时发生,而这通常不是你想要的。请把它们放进函数里。
标准库
Python 自带一个庞大的标准库。你已经见过其中几个了。下面是你会经常用到的:
math:平方根、正弦、对数,以及pi和e等常量。random:随机数、随机选择、打乱顺序。datetime:日期和时间。json:读写 JSON。os、os.path:文件系统路径和操作。pathlib:现代的路径处理方式,通常比os.path更好用。re:正则表达式。collections:专用的集合类型(Counter、defaultdict、deque等)。itertools、functools:迭代器和函数的辅助工具。csv:读写 CSV 文件。
标准库已经装好了,不需要 pip install。
第三方包与 pip
标准库没有覆盖到的功能,可以去 Python Package Index(PyPI)找,那里有几十万个包。用 pip 安装它们:
pip install requests
pip install pandas
pip install --upgrade requests
pip uninstall requests
安装之后,导入方式和其他模块一样:
import requests
response = requests.get("https://api.example.com/data")
print(response.status_code)
几条让包管理保持清爽的建议:
使用虚拟环境
把所有东西都装到全局会带来麻烦。不同的项目需要不同的版本;有些包依赖特定的 Python 版本;在某些系统上,全局安装还需要管理员权限。虚拟环境是一个只给单个项目用的独立 Python 环境:
python3 -m venv .venv
source .venv/bin/activate # macOS/Linux
.venv\Scripts\activate # Windows
pip install requests
当 venv 处于激活状态时,pip 会把包安装到项目文件夹里,而不是你的系统中。用完后用 deactivate 退出。
固定依赖版本
把已安装的包列表保存下来,这样其他人(包括以后的你)就能重建相同的环境:
pip freeze > requirements.txt
之后再运行:
pip install -r requirements.txt
Poetry、pipenv 和 uv 等工具用起来更顺手,但对于学习来说,简单的 pip + requirements.txt 流程完全够用。
编写自己的模块
当你有想在多个文件中复用的函数时,就把它们放进单独的模块。命名约定:
- 文件名使用
lower_snake_case.py。 - 模块级的函数和变量也使用
lower_snake_case。 - 类使用
PascalCase。
# shopping.py
PRICES = {"apple": 0.50, "bread": 2.00}
def total(items):
return sum(PRICES[item] for item in items)
# main.py
from shopping import total, PRICES
print(total(["apple", "bread"]))
print(PRICES)
如果同一文件夹里的两个文件互相导入,你可能会无意中造成循环导入。通常的解决办法是把共用的代码移到第三个文件中,让它们都导入这个文件。
包:装着模块的文件夹
当模块多了起来,就把它们组织成一个包,也就是一个包含 __init__.py 文件的文件夹:
myproject/
├── main.py
└── utils/
├── __init__.py
├── text.py
└── numbers.py
然后:
from utils.text import slugify
from utils import numbers
print(slugify("Hello, World"))
print(numbers.mean([1, 2, 3]))
__init__.py 可以是空的;正是它的存在让 utils 成为一个包。新版 Python 也支持不带 __init__.py 的“命名空间包”,但刚开始学的时候,显式的写法更清楚。
导入出错时的简易检查清单
- “ModuleNotFoundError: No module named X”。要么这个包没有安装,要么你运行 Python 用的解释器和执行
pip install时的不是同一个。如果在 venv 中,请确认 venv 已激活。 - “ImportError: cannot import name Y from X”。模块存在,但你要的名字不在里面。检查是否有拼写错误,并看看模块里实际有什么。
- “Circular import”。两个模块互相导入。把共用的代码移到第三个模块中。
下一步:安装第三方包
靠自己的模块能走很远,但大多数实际项目还会引入 PyPI 上的库,比如 requests、pandas、某个测试框架。下一页介绍 pip、如何干净地安装包,以及如何记录项目的依赖。
常见问题
Python 中的模块是什么?
模块就是任意一个 .py 文件。Python 文件会自动成为模块,你可以按名字(不带 .py)导入它们。标准库是一大批内置模块,比如 math、json 和 datetime,每次安装 Python 都会自带。
import X 和 from X import Y 有什么区别?
import X 导入整个模块,你需要用 X.something 访问其中的内容。from X import Y 只把 Y 引入你的命名空间,所以可以直接用 Y。第一种写法更安全(不会命名冲突);第二种更简洁。
如何安装 Python 包?
在终端中运行 pip install package_name。pip 是 Python 的包管理器,新版 Python 安装时都会自带。为了让项目相互隔离,请先创建一个虚拟环境,这样安装的包就不会在项目之间互相影响。