Skip to content

模块与包

脚本写到一定长度后,会想拆成多个文件——一个放工具函数,一个放数据处理逻辑,一个做入口。模块就是把逻辑拆到不同文件里,包是把多个模块组织成一个目录。拆清楚之后,改一处不用翻整个文件,排查时也知道去哪找。

一、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操作系统接口(环境变量、进程信息)
sysPython 解释器接口(命令行参数、退出码)
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 对应模块名 utilsimport utilsfrom 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

pipuv 安装:

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)

第三方包的引入方式和标准库一样,都是 importfrom ... import ...。区别在于:标准库随 Python 自带,第三方包要先装——在虚拟环境里装,不要往系统 Python 里塞。

关于依赖管理的细节见 Python 是什么与环境 里"依赖隔离"和"项目依赖管理"两节。