====== 第十章 模块与包 ====== 模块和包是Python中组织代码的核心机制。随着程序规模的增长,将代码分割成多个文件和目录变得至关重要。本章将详细介绍模块、包的概念以及如何使用它们来构建可维护的大型项目。 ===== 10.1 模块的概念 ===== 模块是包含Python代码的文件,以.py为扩展名。模块可以定义函数、类和变量,也可以包含可执行的代码。使用模块可以将大程序分解为可管理的部分,提高代码的可重用性和组织性。 ==== 10.1.1 为什么使用模块 ==== 模块提供了多种优势: * **代码复用**:编写一次,多处使用 * **命名空间隔离**:避免命名冲突 * **可维护性**:将相关功能组织在一起 * **抽象**:隐藏实现细节,暴露清晰接口 ==== 10.1.2 创建模块 ==== 创建模块非常简单,只需将Python代码保存为.py文件即可。 **文件:math_utils.py** """数学工具模块 - 提供常用的数学计算功能""" PI = 3.14159265359 E = 2.71828182846 def factorial(n): """计算阶乘""" if n < 0: raise ValueError("n必须是非负整数") if n == 0 or n == 1: return 1 result = 1 for i in range(2, n + 1): result *= i return result def is_prime(n): """判断是否为素数""" if n < 2: return False for i in range(2, int(n**0.5) + 1): if n % i == 0: return False return True def gcd(a, b): """计算最大公约数(欧几里得算法)""" while b: a, b = b, a % b return a def lcm(a, b): """计算最小公倍数""" return abs(a * b) // gcd(a, b) def fibonacci(n): """生成斐波那契数列的前n项""" if n <= 0: return [] elif n == 1: return [0] fib = [0, 1] for i in range(2, n): fib.append(fib[i-1] + fib[i-2]) return fib # 模块级别的代码,在导入时执行 print(f"模块 {__name__} 已加载") ===== 10.2 导入模块 ===== Python提供了多种导入模块的方式,每种方式适用于不同的场景。 ==== 10.2.1 import语句 ==== 最基本的方式是使用import语句导入整个模块。 import math_utils # 使用模块中的函数 print(math_utils.factorial(5)) # 输出: 120 print(math_utils.is_prime(17)) # 输出: True print(math_utils.gcd(48, 18)) # 输出: 6 print(math_utils.PI) # 输出: 3.14159265359 # 生成斐波那契数列 fib_sequence = math_utils.fibonacci(10) print(fib_sequence) # 输出: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] ==== 10.2.2 from...import语句 ==== 从模块中导入特定的函数或变量,可以直接使用而无需模块名前缀。 from math_utils import factorial, is_prime, PI print(factorial(5)) # 直接调用,无需math_utils.前缀 print(is_prime(17)) print(PI) ==== 10.2.3 使用别名 ==== 使用as关键字为模块或函数指定别名,这在模块名较长或避免命名冲突时特别有用。 import math_utils as mu from math_utils import factorial as fact print(mu.gcd(48, 18)) print(fact(5)) # 避免与内置math模块冲突 import math import math_utils as mymath print(math.pi) # 内置math模块 print(mymath.PI) # 自定义math_utils模块 ==== 10.2.4 导入所有内容 ==== 使用星号(*)导入模块中的所有公共名称,但不推荐这种方式,因为它会污染命名空间。 from math_utils import * print(factorial(5)) print(gcd(48, 18)) print(PI) **注意**:星号导入只导入模块的__all__列表中定义的名称,如果没有定义__all__,则导入所有不以 underscore 开头的名称。 ===== 10.3 模块搜索路径 ===== 当导入模块时,Python按照特定顺序在以下位置搜索: 1. 当前目录 2. PYTHONPATH环境变量中的目录 3. 标准库目录 4. 第三方包目录(site-packages) import sys # 查看模块搜索路径 for path in sys.path: print(path) # 动态添加搜索路径 sys.path.append('/path/to/my/modules') sys.path.insert(0, '/priority/path') # 添加到最前面 ===== 10.4 模块的特殊属性 ===== ==== 10.4.1 __name__属性 ==== __name__属性在模块被直接运行时值为'__main__',在被导入时为模块名。这常用于编写既可导入又可运行的模块。 # 在math_utils.py末尾添加 if __name__ == "__main__": # 测试代码,仅在直接运行时执行 print("运行测试...") assert factorial(0) == 1 assert factorial(5) == 120 assert is_prime(2) == True assert is_prime(4) == False assert gcd(48, 18) == 6 print("所有测试通过!") ==== 10.4.2 __all__属性 ==== __all__列表定义了当使用"from module import *"时应导入的名称。 # 在math_utils.py开头添加 __all__ = ['factorial', 'is_prime', 'gcd', 'lcm', 'fibonacci', 'PI', 'E'] ==== 10.4.3 其他常用属性 ==== import math_utils print(math_utils.__name__) # 模块名 print(math_utils.__file__) # 模块文件路径 print(math_utils.__doc__) # 模块文档字符串 print(dir(math_utils)) # 模块中所有属性和方法 ===== 10.5 包的概念 ===== 包是包含多个模块的目录,通过层次化的方式组织代码。包目录必须包含一个__init__.py文件(Python 3.3+可选,但建议保留)。 ==== 10.5.1 包的结构 ==== 典型的包结构如下: myproject/ ├── __init__.py ├── core/ │ ├── __init__.py │ ├── models.py │ └── utils.py ├── web/ │ ├── __init__.py │ ├── routes.py │ └── templates/ └── db/ ├── __init__.py ├── connection.py └── queries.py ==== 10.5.2 __init__.py文件 ==== __init__.py文件在包被导入时执行,常用于: * 初始化包级别的变量 * 控制包的导入行为 * 简化包的公共接口 **文件:mypackage/__init__.py** """mypackage - 示例包""" __version__ = '1.0.0' __author__ = 'Python Developer' # 简化导入,让用户可以直接 from mypackage import some_function from .core.utils import helper_function from .web.routes import setup_routes # 定义__all__控制星号导入 __all__ = ['helper_function', 'setup_routes', '__version__'] ===== 10.6 相对导入与绝对导入 ===== ==== 10.6.1 绝对导入 ==== 从项目的根目录开始的完整导入路径。 # 绝对导入 from mypackage.core.models import User from mypackage.db.connection import Database ==== 10.6.2 相对导入 ==== 使用点号表示相对位置,适用于包内部模块之间的导入。 # 在 mypackage/web/routes.py 中 from ..core.models import User # 上级目录的core模块 from ..db.connection import Database from .templates import render # 同级目录的templates模块 # . 当前目录 # .. 上级目录 # ...上上级目录,以此类推 **注意**:相对导入只能在包内部使用,不能直接运行包含相对导入的模块。 ===== 10.7 常用标准库模块 ===== Python标准库包含大量实用的模块: ==== 10.7.1 os模块 - 操作系统接口 ==== import os # 文件和目录操作 print(os.getcwd()) # 获取当前工作目录 os.chdir('/tmp') # 改变工作目录 os.mkdir('newdir') # 创建目录 os.makedirs('a/b/c', exist_ok=True) # 递归创建目录 os.remove('file.txt') # 删除文件 os.rmdir('empty_dir') # 删除空目录 os.rename('old.txt', 'new.txt') # 重命名 # 路径操作 path = os.path.join('folder', 'subfolder', 'file.txt') print(os.path.exists(path)) # 检查路径是否存在 print(os.path.isfile(path)) # 是否为文件 print(os.path.isdir(path)) # 是否为目录 print(os.path.basename(path)) # 文件名 print(os.path.dirname(path)) # 目录名 print(os.path.splitext(path)) # 分离扩展名 # 环境变量 print(os.environ.get('HOME')) os.environ['MY_VAR'] = 'value' ==== 10.7.2 sys模块 - 系统相关 ==== import sys print(sys.version) # Python版本 print(sys.platform) # 平台标识 print(sys.argv) # 命令行参数 sys.exit(0) # 退出程序 # 标准输入输出 sys.stdout.write('Hello\\n') sys.stderr.write('Error message\\n') ==== 10.7.3 datetime模块 - 日期时间 ==== from datetime import datetime, date, timedelta import time # 当前时间 now = datetime.now() print(now.strftime('%Y-%m-%d %H:%M:%S')) # 创建特定日期 d = date(2024, 1, 1) print(d.weekday()) # 星期几 (0=周一) # 时间差 delta = timedelta(days=5, hours=3) future = now + delta past = now - delta # 时间戳转换 timestamp = time.time() dt_from_ts = datetime.fromtimestamp(timestamp) ==== 10.7.4 json模块 - JSON处理 ==== import json data = { 'name': '张三', 'age': 30, 'skills': ['Python', 'JavaScript'], 'address': { 'city': '北京', 'zipcode': '100000' } } # 编码为JSON字符串 json_str = json.dumps(data, ensure_ascii=False, indent=2) print(json_str) # 写入文件 with open('data.json', 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) # 解码JSON parsed = json.loads(json_str) # 从文件读取 with open('data.json', 'r', encoding='utf-8') as f: data_from_file = json.load(f) ===== 10.8 第三方包管理 ===== ==== 10.8.1 pip包管理器 ==== pip是Python的标准包管理工具。 # 安装包 pip install requests pip install numpy pandas matplotlib # 指定版本 pip install requests==2.28.1 pip install 'requests>=2.28.0' # 从requirements.txt安装 pip install -r requirements.txt # 卸载包 pip uninstall requests # 列出已安装包 pip list pip freeze # 显示包信息 pip show requests ==== 10.8.2 requirements.txt ==== 记录项目依赖及其版本: requests==2.28.1 numpy>=1.21.0 pandas>=1.3.0 matplotlib>=3.4.0 生成requirements.txt: pip freeze > requirements.txt ===== 10.9 虚拟环境 ===== 虚拟环境用于隔离不同项目的依赖。 ==== 10.9.1 venv模块 ==== # 创建虚拟环境 python -m venv myenv # 激活虚拟环境 # Linux/Mac: source myenv/bin/activate # Windows: myenv\\Scripts\\activate # 退出虚拟环境 deactivate ==== 10.9.2 conda环境 ==== # 创建环境 conda create -n myenv python=3.11 # 激活环境 conda activate myenv # 退出环境 conda deactivate # 删除环境 conda remove -n myenv --all ===== 10.10 模块发布 ===== 将模块打包发布到PyPI供他人使用。 ==== 10.10.1 项目结构 ==== mypackage/ ├── mypackage/ │ ├── __init__.py │ └── module.py ├── tests/ │ └── test_module.py ├── setup.py ├── README.md ├── LICENSE └── MANIFEST.in ==== 10.10.2 setup.py配置 ==== from setuptools import setup, find_packages setup( name='mypackage', version='1.0.0', packages=find_packages(), install_requires=[ 'requests>=2.25.0', 'numpy>=1.20.0', ], author='Your Name', author_email='your@email.com', description='A short description', long_description=open('README.md').read(), long_description_content_type='text/markdown', url='https://github.com/username/mypackage', classifiers=[ 'Programming Language :: Python :: 3', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', ], python_requires='>=3.8', ) ==== 10.10.3 构建和发布 ==== # 安装构建工具 pip install setuptools wheel twine # 构建包 python setup.py sdist bdist_wheel # 上传到PyPI测试环境 python -m twine upload --repository testpypi dist/* # 上传到正式PyPI python -m twine upload dist/* ===== 10.11 命名空间包 ===== Python 3.3+支持命名空间包,允许将一个大包分割到多个目录。 # 无需__init__.py的命名空间包 # 项目结构: # /path1/ns/pkg1.py # /path2/ns/pkg2.py import sys sys.path.extend(['/path1', '/path2']) import ns.pkg1 import ns.pkg2 ===== 10.12 本章习题 ===== **习题1:模块创建** 创建一个string_utils.py模块,包含以下函数: * reverse_string(s) - 反转字符串 * count_vowels(s) - 统计元音字母数量 * is_palindrome(s) - 判断是否为回文 * word_count(s) - 统计单词数量 * to_camel_case(s) - 下划线命名转驼峰命名 编写适当的文档字符串和__all__列表。 **习题2:包结构** 为一个小型电商系统设计包结构,包含: * 用户管理模块(注册、登录、资料) * 商品管理模块(商品信息、库存) * 订单管理模块(购物车、下单、支付) * 工具模块(邮件、短信、日志) 绘制目录结构图并实现基本的__init__.py文件。 **习题3:导入练习** 给定以下包结构: project/ ├── app/ │ ├── __init__.py │ ├── main.py │ └── config.py ├── utils/ │ ├── __init__.py │ ├── helpers.py │ └── validators.py └── tests/ └── test_helpers.py 写出在以下情况下正确的导入语句: 1. 在app/main.py中导入utils/helpers.py的函数 2. 在utils/helpers.py中导入app/config.py的配置 3. 在tests/test_helpers.py中导入utils/helpers.py **习题4:实际应用** 创建一个命令行工具包cli_tools,包含: * cli_tools/file_ops.py - 文件批量处理(重命名、复制、删除) * cli_tools/text_ops.py - 文本处理(搜索替换、统计) * cli_tools/__main__.py - 入口点,支持命令行参数 实现功能并确保可以通过"python -m cli_tools"运行。 **习题5:虚拟环境实践** 创建一个新的虚拟环境,安装以下包并生成requirements.txt: * requests * beautifulsoup4 * lxml * pillow 编写一个脚本,自动检查并安装requirements.txt中的所有依赖。 ===== 10.13 总结 ===== 本章学习了: * **模块**:Python代码的组织单元,通过import语句使用 * **导入方式**:import、from...import、别名、星号导入 * **模块搜索路径**:sys.path控制模块查找位置 * **特殊属性**:__name__、__all__、__file__等的用途 * **包**:层次化的模块组织方式,使用__init__.py初始化 * **相对导入与绝对导入**:包内部模块间的引用方式 * **标准库模块**:os、sys、datetime、json等常用模块 * **包管理**:pip的使用和requirements.txt * **虚拟环境**:venv和conda环境管理 * **模块发布**:打包和发布到PyPI的流程 掌握模块和包的使用是编写专业Python代码的基础,良好的代码组织能够显著提高项目的可维护性和协作效率。