Appearance
模块与包
脚本写到一定长度后,会想拆成多个文件——一个放工具函数,一个放数据处理逻辑,一个做入口。模块就是把逻辑拆到不同文件里,包是把多个模块组织成一个目录。拆清楚之后,改一处不用翻整个文件,排查时也知道去哪找。
一、import 导入
Python 用 import 把别的文件(模块)里的代码引进来:
python
# 从标准库引入
import json
from pathlib import Path
# 用标准库的 json 模块解析 JSON 字串
data = json.loads('{"title": "Python 入门"}')
print(data["title"]) # Python 入门
# 用 pathlib 的 Path 读文件
content = Path("notes.txt").read_text(encoding="utf-8")import json 引进整个 json 模块,调用时写 json.loads();from pathlib import Path 只引进 Path 这个类,调用时直接写 Path()。
两种写法的选择:模块名短、里面只有一个常用对象,用 from ... import ... 更省字;模块名长、里面有很多功能,用 import ... 保持命名空间清晰。比如 json.dumps() json.loads() 比 from json import dumps, loads 稍多字,但一眼就知道这些函数来自 json——dumps() 和 loads() 这种名字太通用,不留模块名前缀容易混。
二、标准库
Python 安装时就带了一批常用模块,叫标准库。不用额外安装,直接 import 就能用:
| 模块 | 常见用途 |
|---|---|
pathlib | 文件路径拼接、读写、判断是否存在 |
json | 解析和生成 JSON |
csv | 读 CSV 文件 |
re | 正则表达式 |
datetime | 时间和日期处理 |
math | 数学运算(三角函数、对数等) |
random | 随机数、随机选择 |
os | 操作系统接口(环境变量、进程信息) |
sys | Python 解释器接口(命令行参数、退出码) |
subprocess | 执行外部命令 |
logging | 日志输出 |
collections | 扩展的数据结构(Counter、deque) |
标准库覆盖了大部分脚本场景——文件、时间、随机数、JSON、CSV、命令执行这些都有。先查标准库有没有,再去装第三方包——很多一开始想装 requests 的场景,标准库的 urllib.request 其实够用;一开始想装 python-dateutil 的场景,标准库的 datetime 加一点手写逻辑也能搞定。
三、自定义模块
新建一个文件 utils.py,里面写几个函数:
python
# utils.py
"""通用工具函数。"""
def format_title(title, max_length=20):
"""截断标题,超出部分用省略号补。"""
if len(title) > max_length:
return title[:max_length] + "..."
return title
def count_words(text):
"""统计文本里的单词数(按空格切)。"""
return len(text.split())在另一个脚本里引入:
python
# main.py
from utils import format_title, count_words
title = "这是一篇很长很长很长的文章标题"
print(format_title(title, max_length=10))
# 这是一篇很长很...
print(count_words("Python is a great language"))
# 4一个 .py 文件就是一个模块,文件名就是模块名(去掉 .py 后缀)。utils.py 对应模块名 utils,import utils 或 from utils import ... 都能引进来。
模块拆分的原则:一个模块干一类事,名字跟内容对得上。utils.py 放通用工具,article.py 放文章相关函数,contact.py 放联系人相关函数——不要把所有东西堆进一个文件,也不要一个文件里塞三四种完全不相关的逻辑。
四、__name__ 与入口
模块被别的文件引入时,有些代码不应该自动执行——比如测试代码、示例调用。__name__ 可以区分这两种情况:
python
# utils.py
def format_title(title, max_length=20):
if len(title) > max_length:
return title[:max_length] + "..."
return title
# 只有直接运行这个文件时才执行下面这段
if __name__ == "__main__":
# 简单测试
print(format_title("短标题"))
print(format_title("这是一篇很长很长的标题", max_length=10))直接运行 python utils.py 时,__name__ 是 "__main__",测试代码会执行;被别的文件 import utils 时,__name__ 是 "utils",测试代码不执行。
这个写法是模块的标准模板——工具模块底部加一段 if __name__ == "__main__":,放简单测试或示例。后面别人引入你的模块不会被测试代码干扰,你自己跑这个文件又能快速验证。
五、包
模块多了之后,可以按功能分目录,叫包。一个目录里放多个 .py 文件,再加一个 __init__.py(内容可以是空文件),这个目录就是一个包:
text
notes/
├── __init__.py
├── article.py # 文章相关函数
└── contact.py # 联系人相关函数引入包里的模块:
python
# 从 notes 包引入 article 模块
from notes.article import format_title
from notes.contact import build_contact_list
print(format_title("长标题长标题长标题", max_length=8))
# 长标题长标题...
contacts = build_contact_list(["张三", "李四"])
print(contacts)__init__.py 可以空着,也可以写一些包级别的初始化代码——比如引入子模块、定义包级常量。Python 3.3 之后不加 __init__.py 也能当包用,但加上更明确——一眼就知道这个目录是包,不是随便放的文件夹。
包的命名原则:包名用短单词或小写加下划线,跟模块名风格一致。notes article_tools contact_utils 都可以;NoteTools ArticlePack 这种驼峰命名不建议——Python 社区习惯全小写。
六、第三方包
标准库覆盖了基础场景,但有些功能需要额外装包——比如 HTTP 请求用 requests、YAML 解析用 pyyaml、SSH 连接用 paramiko。
用 pip 或 uv 安装:
bash
# 用 pip 安装
pip install requests
# 用 uv 安装(推荐)
uv add requests安装后就能引入:
python
import requests
response = requests.get("https://example.com/api/articles")
data = response.json()
print(data)第三方包的引入方式和标准库一样,都是 import 或 from ... import ...。区别在于:标准库随 Python 自带,第三方包要先装——在虚拟环境里装,不要往系统 Python 里塞。
关于依赖管理的细节见 Python 是什么与环境 里"依赖隔离"和"项目依赖管理"两节。