07|模块化架构与依赖管理:复用生态中的现有成果
场景引入:项目规模膨胀后的代码管理困境
你的自动化脚本从最初的一个文件、50 行代码,逐渐扩展到了处理数据库查询、发送通知邮件、生成报表等多个功能。所有代码堆在一个文件里,已经超过 2000 行——查找一个函数要滚动半天,修改一处逻辑担心影响其他部分。
解决方案是模块化:将代码按功能拆分到不同文件中,各司其职、互相调用。Python 的模块(module)和包(package)机制正是为此而生。
此外,Python 生态中已有数十万个第三方库覆盖了绝大多数常见需求。学会发现、安装和管理这些库,能让你避免重复开发,将精力集中在真正的业务逻辑上。
模块的本质
**每个独立的 .py 文件都构成一个模块。**通过模块,你可以将代码拆分到多个文件中,在需要的地方导入使用。
假设你创建了一个 calc_utils.py 文件:
# calc_utils.py
def margin(revenue, cost):
"""计算利润率"""
return (revenue - cost) / revenue
def compound(principal, rate, periods):
"""复利计算"""
return principal * (1 + rate) ** periods
EULER = 2.71828
在其他文件中即可导入:
# app.py
import calc_utils
print(calc_utils.margin(1000, 700)) # 0.3
print(calc_utils.compound(10000, 0.05, 3)) # 11576.25
print(calc_utils.EULER) # 2.71828
import 的多种方式
基础导入方式
# 方式一:导入整个模块
import math
print(math.sqrt(49)) # 7.0
print(math.pi) # 3.141592653589793
# 方式二:从模块中导入特定成员
from math import sqrt, pi
print(sqrt(49)) # 7.0(不需要 math. 前缀)
print(pi)
# 方式三:导入全部(不推荐——可能导致命名冲突)
from math import *
print(cos(0)) # 1.0
# 方式四:为模块设置别名
import numpy as np
import pandas as pd
# 方式五:为导入成员设置别名
from datetime import datetime as dt
current = dt.now()
**最佳实践:**避免使用 from xxx import *。它会将模块中所有公开名称引入当前命名空间,你无法确定引入了什么,容易产生命名冲突。
导入的执行过程
执行 import calc_utils 时,Python 依次完成以下步骤:
- 查找模块文件
- 编译为字节码(生成
__pycache__目录下的.pyc文件) - 执行模块中的顶层代码(仅首次导入时执行)
- 将模块对象绑定到当前命名空间
模块搜索路径
Python 导入模块时按以下优先级搜索:
- 内置模块(
sys、os等) - 当前脚本所在目录
PYTHONPATH环境变量中指定的目录- 标准库目录
- 第三方包目录(
site-packages)
查看完整搜索路径:
import sys
for entry in sys.path:
print(entry)
如需添加自定义搜索路径:
import sys
sys.path.append('/opt/custom_libs')
__name__ 与 '__main__' 机制
每个模块都有一个内置属性 __name__:
- 直接执行该文件时,Python 会将
__name__设为'__main__' - 模块被其他文件导入时,
__name__的值是模块文件名(不含.py)
# notifier.py
def send_alert(target, msg):
print(f'[ALERT -> {target}] {msg}')
def send_report(target, content):
print(f'[REPORT -> {target}] {content}')
if __name__ == '__main__':
# 仅直接运行 notifier.py 时执行
send_alert('ops_team', '测试告警')
send_report('manager', '测试报告')
# 直接运行:测试代码执行
$ python notifier.py
[ALERT -> ops_team] 测试告警
[REPORT -> manager] 测试报告
# 在另一个文件中导入:测试代码不执行
import notifier
notifier.send_alert('dev_team', '部署完成')
这个机制让一个文件既可以作为可复用的模块被导入,也可以作为独立脚本直接运行。
创建规范的自定义模块
模块模板
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
数据校验工具模块
提供常用的数据验证函数。
"""
__author__ = 'dev_team'
__version__ = '1.0'
# ---- 公开接口 ----
def validate_email(addr):
"""检查邮箱格式是否合法"""
return '@' in addr and '.' in addr.split('@')[-1]
def validate_range(val, lo, hi):
"""检查数值是否在指定范围内"""
return lo <= val <= hi
# ---- 内部实现(以下划线开头)----
def _normalize(text):
"""内部辅助函数,外部不应直接调用"""
return text.strip().lower()
# ---- 主入口 ----
if __name__ == '__main__':
print(validate_email('user@domain.com')) # True
print(validate_range(42, 0, 100)) # True
命名约定
# 公开接口:正常命名
def process_data(raw):
return _clean(raw)
THRESHOLD = 0.95
# 内部使用:以 _ 开头(约定,非强制隔离)
def _clean(raw):
return raw.strip()
_internal_state = {}
# 特殊属性:双下划线开头和结尾
__author__ = 'team_a'
__version__ = '2.1'
Python 不强制私有性。_ 开头的名称只是一种社区约定,意为”这是内部实现,请勿直接依赖”。
包(package):多模块的目录组织
当模块数量增多时,需要用包来组织它们。包本质上是一个包含 __init__.py 文件的目录。
包的目录结构
automation/
__init__.py # 标识这是一个包
data_loader.py # automation.data_loader
report_builder.py # automation.report_builder
notifications/ # 子包
__init__.py
email_sender.py # automation.notifications.email_sender
webhook.py # automation.notifications.webhook
__init__.py 的作用
# automation/__init__.py
# 定义包级别的变量
PACKAGE_VERSION = '2.0.0'
# 控制 from automation import * 的导入范围
__all__ = ['data_loader', 'report_builder']
# 可以执行初始化逻辑
print('automation 包已加载')
导入包中的模块
# 导入整个模块
import automation.data_loader
automation.data_loader.load_csv('input.csv')
# 导入特定模块
from automation import report_builder
report_builder.generate()
# 导入模块中的特定函数
from automation.data_loader import load_csv
load_csv('input.csv')
# 导入子包
from automation.notifications import email_sender
email_sender.dispatch('admin', '报告已生成')
pip:第三方库的安装与管理
第 1 章已经介绍过 pip 的基本用法和虚拟环境的概念。这里将更深入地讲解依赖管理的工程实践。
核心命令
# 安装库
pip install flask
# 安装指定版本
pip install flask==2.3.0
# 升级库
pip install --upgrade flask
# 卸载库
pip uninstall flask
# 列出已安装的库
pip list
# 查看库的详细信息
pip show flask
使用镜像加速
# 临时使用镜像
pip install flask -i https://pypi.tuna.tsinghua.edu.cn/simple
# 永久设置
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
requirements.txt 管理依赖
# 导出当前环境的依赖清单
pip freeze > requirements.txt
# 根据清单安装所有依赖(部署时常用)
pip install -r requirements.txt
requirements.txt 内容示例:
flask==3.1.3
sqlalchemy>=2.0.0
httpx~=0.28.0
版本约束运算符说明:
==2.3.2:精确匹配指定版本>=2.0.0:允许 2.0.0 及以上任意版本~=0.24.0:兼容版本,等价于>=0.24.0, <0.25.0——允许补丁版本升级,但不允许次版本变更
虚拟环境 venv:隔离项目依赖
不同项目可能依赖同一个库的不同版本。虚拟环境为每个项目提供独立的包安装空间。
# 创建虚拟环境
python -m venv env
# 激活
# macOS / Linux:
source env/bin/activate
# Windows:
env\Scripts\activate
# 激活后提示符会变为 (env) $
# 此时安装的库仅对当前环境生效
pip install flask
# 退出虚拟环境
deactivate
推荐工作流:
# 1. 为新项目创建虚拟环境
mkdir analytics_app && cd analytics_app
python -m venv env
# 2. 激活环境
source env/bin/activate
# 3. 安装依赖
pip install flask sqlalchemy
# 4. 锁定依赖
pip freeze > requirements.txt
# 5. 完成后退出
deactivate
将 env/ 目录加入 .gitignore,不要提交到版本控制。
常用标准库精选
Python 自带两百多个标准库模块,以下是开发中使用频率最高的几个。
os——操作系统接口
import os
# 环境变量
print(os.environ.get('HOME'))
print(os.environ.get('PATH'))
# 文件和目录操作
os.makedirs('output/archive', exist_ok=True)
os.rename('old_name.txt', 'new_name.txt')
os.remove('temp.txt')
os.listdir('.')
# 当前目录
print(os.getcwd())
sys——解释器相关
import sys
print(sys.version) # Python 版本
print(sys.platform) # 操作系统标识
print(sys.argv) # 命令行参数列表
print(sys.path) # 模块搜索路径
sys.exit(0) # 退出程序
json——JSON 序列化
import json
payload = {'action': 'deploy', 'targets': ['srv01', 'srv02']}
serialized = json.dumps(payload, ensure_ascii=False, indent=2)
print(serialized)
deserialized = json.loads('{"status": "ok", "code": 200}')
print(deserialized['status'])
datetime——日期时间处理
from datetime import datetime, timedelta
now = datetime.now()
print(now)
print(now.strftime('%Y-%m-%d %H:%M'))
# 创建指定时间
launch_date = datetime(2025, 6, 1, 9, 0)
# 时间运算
next_week = now + timedelta(weeks=1)
yesterday = now - timedelta(days=1)
# 计算时间差
elapsed = now - launch_date
print(f'距发布已过 {elapsed.days} 天')
# 字符串解析为日期
parsed = datetime.strptime('2025-06-01', '%Y-%m-%d')
random——随机数生成
import random
print(random.random()) # 0 到 1 之间的浮点数
print(random.randint(1, 100)) # 1 到 100 之间的整数
print(random.uniform(5.0, 15.0)) # 5.0 到 15.0 之间的浮点数
options = ['plan_a', 'plan_b', 'plan_c', 'plan_d']
print(random.choice(options)) # 随机选一个
print(random.sample(options, 2)) # 随机选两个(不重复)
deck = list(range(1, 53))
random.shuffle(deck) # 打乱顺序
collections——增强数据结构
from collections import Counter, defaultdict, namedtuple, deque
# Counter:频率统计
events = ['login', 'click', 'login', 'scroll', 'click', 'login']
freq = Counter(events)
print(freq) # Counter({'login': 3, 'click': 2, 'scroll': 1})
print(freq.most_common(2)) # [('login', 3), ('click', 2)]
# defaultdict:自动初始化的字典
grouped = defaultdict(list)
grouped['backend'].append('Python')
grouped['backend'].append('Go')
grouped['frontend'].append('React')
print(dict(grouped))
# namedtuple:具名元组
Endpoint = namedtuple('Endpoint', ['host', 'port', 'protocol'])
ep = Endpoint('10.0.1.5', 8080, 'https')
print(ep.host, ep.port)
# deque:双端队列
task_queue = deque(['task_1', 'task_2', 'task_3'])
task_queue.appendleft('urgent_task')
task_queue.append('task_4')
processed = task_queue.popleft()
print(processed) # urgent_task
本章回顾
| 概念 | 说明 |
|---|---|
| 模块 | 每个独立的 .py 源文件即为一个模块 |
| 包 | 包含 __init__.py 的目录,组织多个模块 |
| import | 导入模块,支持多种导入方式 |
__name__ | 作为脚本执行时值为 '__main__',作为模块导入时值为文件名 |
| pip | 第三方库的安装和管理工具 |
| venv | 虚拟环境,隔离项目间的依赖 |
| 标准库 | Python 自带的丰富工具集 |
模块化开发的收益:
- 代码复用:编写一次,多处调用
- 命名空间隔离:各模块拥有独立的命名空间,同名变量不会冲突
- 可维护性:按功能域划分文件,维护时定位精准
- 团队协作:不同成员可并行开发不同模块
补充:常见的模块组织模式
在中等规模的项目中,以下目录结构是一种被广泛采用的实践:
my_project/
src/
__init__.py
core/
__init__.py
engine.py
scheduler.py
utils/
__init__.py
formatter.py
validator.py
api/
__init__.py
routes.py
middleware.py
tests/
test_engine.py
test_formatter.py
requirements.txt
setup.py
关键原则:
- 按功能域组织代码,而非按文件类型
core/存放核心业务逻辑utils/存放通用辅助函数api/存放对外接口tests/与src/平级,存放测试代码
补充:模块的条件导入
有时你需要根据运行环境选择不同的库实现:
# 优先使用高性能的第三方库,不可用时回退到标准库
try:
import ujson as json_lib
except ImportError:
import json as json_lib
data = json_lib.loads('{"status": "ok"}')
print(data)
补充:
ujson之外,orjson是当前更流行的高性能 JSON 库,序列化速度更快且支持更多数据类型(如datetime、numpy数组)。新项目中可以优先考虑orjson。
这种模式在需要兼容多种环境的库代码中非常常见。
核心理念:**在动手实现一个功能之前,先查找是否已有现成的库可以直接使用。**Python 生态的丰富程度意味着大部分常见需求都已被高质量地实现过。
下一章将进入面向对象编程——学习用类和对象来组织更复杂的程序结构。
购买课程解锁全部内容
零基础到独立开发:Python 自动化与 Web 实战
¥29.90