跳到主要内容
预计阅读 25 分钟

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 依次完成以下步骤:

  1. 查找模块文件
  2. 编译为字节码(生成 __pycache__ 目录下的 .pyc 文件)
  3. 执行模块中的顶层代码(仅首次导入时执行)
  4. 将模块对象绑定到当前命名空间

模块搜索路径

Python 导入模块时按以下优先级搜索:

  1. 内置模块sysos 等)
  2. 当前脚本所在目录
  3. PYTHONPATH 环境变量中指定的目录
  4. 标准库目录
  5. 第三方包目录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 库,序列化速度更快且支持更多数据类型(如 datetimenumpy 数组)。新项目中可以优先考虑 orjson

这种模式在需要兼容多种环境的库代码中非常常见。

核心理念:**在动手实现一个功能之前,先查找是否已有现成的库可以直接使用。**Python 生态的丰富程度意味着大部分常见需求都已被高质量地实现过。

下一章将进入面向对象编程——学习用类和对象来组织更复杂的程序结构。

购买课程解锁全部内容

零基础到独立开发:Python 自动化与 Web 实战

¥29.90