Appearance
模块与包
模块(Module)和包(Package)是Python组织代码的重要方式。通过模块化编程,可以将代码分割成多个文件,提高代码的可维护性和复用性。本章将详细介绍Python中的模块和包的使用方法。
模块基础
什么是模块
模块是一个包含Python代码的文件,文件名就是模块名(去掉.py后缀)。模块可以包含函数、类、变量和可执行语句。
python
# 模块的基本概念
# 假设我们有一个文件 my_module.py,内容如下:
"""
my_module.py - 示例模块
这是一个演示模块基本结构的示例
"""
# 模块级别的变量
MODULE_NAME = "我的模块"
VERSION = "1.0.0"
# 模块级别的函数
def greet(name):
"""问候函数"""
return f"你好,{name}!"
# 模块级别的类
class Calculator:
"""简单计算器类"""
@staticmethod
def add(a, b):
return a + b
@staticmethod
def subtract(a, b):
return a - b
# 模块级别的可执行代码
if __name__ == "__main__":
# 这段代码只在直接运行模块时执行
print(f"模块 {MODULE_NAME} v{VERSION}")
print(greet("世界"))
print(f"1 + 2 = {Calculator.add(1, 2)}")导入模块
python
# 导入模块的多种方式
# 1. 导入整个模块
import math
# 使用模块中的内容
print(f"π的值: {math.pi}")
print(f"sin(π/2): {math.sin(math.pi / 2)}")
# 2. 导入模块并设置别名
import numpy as np # 常见的别名约定
import pandas as pd
# 使用别名
# arr = np.array([1, 2, 3])
# 3. 从模块导入特定内容
from math import pi, sin, cos
# 直接使用导入的内容
print(f"π = {pi}")
print(f"cos(0) = {cos(0)}")
# 4. 从模块导入所有内容(不推荐)
from math import *
# 这样可以直接使用所有 math 模块的内容
# 但容易造成命名冲突,不推荐使用
# 5. 导入并重命名
from math import pi as PI
print(f"PI = {PI}")
# 6. 导入多个模块
import os, sys, json
print(f"当前目录: {os.getcwd()}")
print(f"Python版本: {sys.version}")
# 7. 条件导入
try:
import custom_module
except ImportError:
print("自定义模块未安装,使用默认配置")
custom_module = None模块搜索路径
python
# 模块搜索路径
import sys
# 1. 查看模块搜索路径
print("模块搜索路径:")
for i, path in enumerate(sys.path):
print(f" {i}: {path}")
# 2. 添加自定义搜索路径
# sys.path.append("/path/to/your/modules")
# 3. 查看已导入的模块
print("\n已导入的模块(部分):")
for module_name in list(sys.modules.keys())[:10]:
print(f" {module_name}")
# 4. 查看模块信息
import math
print(f"\nmath模块信息:")
print(f" 文件路径: {math.__file__}")
print(f" 模块名: {math.__name__}")
print(f" 模块文档: {math.__doc__[:50]}...")
# 5. 使用 importlib 动态导入
import importlib
# 动态导入模块
json_module = importlib.import_module("json")
data = json_module.dumps({"name": "张三"})
print(f"\n动态导入json模块: {data}")
# 重新加载模块(开发时有用)
# importlib.reload(some_module)创建模块
模块的结构
python
# 创建一个完整的模块示例
# 文件: calculator.py
"""
calculator - 计算器模块
提供基本的数学运算功能
示例:
from calculator import Calculator
calc = Calculator()
result = calc.add(1, 2)
"""
# 模块常量
VERSION = "1.0.0"
AUTHOR = "Python教程"
# 私有变量(约定)
_internal_value = 42
# 公开变量
DEFAULT_PRECISION = 2
def format_result(value, precision=None):
"""格式化计算结果
Args:
value: 要格式化的值
precision: 小数位数,默认使用 DEFAULT_PRECISION
Returns:
格式化后的字符串
"""
if precision is None:
precision = DEFAULT_PRECISION
return f"{value:.{precision}f}"
class Calculator:
"""计算器类
提供基本的数学运算功能。
Attributes:
history: 计算历史记录列表
"""
def __init__(self):
"""初始化计算器"""
self.history = []
def add(self, a, b):
"""加法运算"""
result = a + b
self.history.append(f"{a} + {b} = {result}")
return result
def subtract(self, a, b):
"""减法运算"""
result = a - b
self.history.append(f"{a} - {b} = {result}")
return result
def multiply(self, a, b):
"""乘法运算"""
result = a * b
self.history.append(f"{a} * {b} = {result}")
return result
def divide(self, a, b):
"""除法运算"""
if b == 0:
raise ValueError("除数不能为零")
result = a / b
self.history.append(f"{a} / {b} = {result}")
return result
def get_history(self):
"""获取计算历史"""
return self.history.copy()
def clear_history(self):
"""清空计算历史"""
self.history.clear()
# 快捷函数
def quick_add(a, b):
"""快速加法"""
return Calculator().add(a, b)
# 模块测试代码
if __name__ == "__main__":
print(f"计算器模块 v{VERSION}")
print(f"作者: {AUTHOR}")
calc = Calculator()
print(f"1 + 2 = {calc.add(1, 2)}")
print(f"10 - 3 = {calc.subtract(10, 3)}")
print(f"4 * 5 = {calc.multiply(4, 5)}")
print(f"20 / 4 = {calc.divide(20, 4)}")
print(f"计算历史: {calc.get_history()}")模块的 name 属性
python
# __name__ 属性的使用
# 当模块被直接运行时,__name__ == "__main__"
# 当模块被导入时,__name__ == 模块名
def main():
"""主函数"""
print("这是主程序")
def helper_function():
"""辅助函数"""
print("这是辅助函数")
# 只有直接运行时才执行主函数
if __name__ == "__main__":
main()
else:
# 被导入时的初始化代码
print("模块被导入")
# 实际应用示例
class AppConfig:
"""应用配置"""
def __init__(self):
self.debug = False
self.database_url = "sqlite:///app.db"
def load_from_env(self):
"""从环境变量加载配置"""
import os
self.debug = os.getenv("DEBUG", "false").lower() == "true"
self.database_url = os.getenv("DATABASE_URL", self.database_url)
# 创建默认配置实例
config = AppConfig()
# 如果是主程序,加载环境配置
if __name__ == "__main__":
config.load_from_env()
print(f"Debug模式: {config.debug}")
print(f"数据库URL: {config.database_url}")包(Package)
什么是包
包是一个包含多个模块的目录,目录下必须有一个 __init__.py 文件。包可以嵌套,形成层级结构。
mypackage/
├── __init__.py # 包初始化文件
├── module1.py # 模块1
├── module2.py # 模块2
└── subpackage/ # 子包
├── __init__.py
└── module3.py创建包
python
# 创建包的示例结构
# 假设我们有以下目录结构:
"""
myapp/
├── __init__.py
├── config.py
├── utils/
│ ├── __init__.py
│ ├── string_utils.py
│ └── math_utils.py
└── models/
├── __init__.py
├── user.py
└── product.py
"""
# myapp/__init__.py
"""
myapp - 示例应用包
"""
from .config import Config
from .utils import string_utils, math_utils
__version__ = "1.0.0"
__all__ = ["Config", "string_utils", "math_utils"]
# myapp/config.py
"""配置模块"""
class Config:
"""配置类"""
def __init__(self):
self.debug = False
self.database_url = "sqlite:///app.db"
# myapp/utils/__init__.py
"""工具包"""
from .string_utils import capitalize_words
from .math_utils import calculate_average
__all__ = ["capitalize_words", "calculate_average"]
# myapp/utils/string_utils.py
"""字符串工具模块"""
def capitalize_words(text):
"""将每个单词首字母大写"""
return " ".join(word.capitalize() for word in text.split())
# myapp/utils/math_utils.py
"""数学工具模块"""
def calculate_average(numbers):
"""计算平均值"""
if not numbers:
return 0
return sum(numbers) / len(numbers)
# myapp/models/__init__.py
"""模型包"""
from .user import User
from .product import Product
__all__ = ["User", "Product"]
# myapp/models/user.py
"""用户模型"""
class User:
"""用户类"""
def __init__(self, name, email):
self.name = name
self.email = email
def __repr__(self):
return f"User(name='{self.name}', email='{self.email}')"
# myapp/models/product.py
"""产品模型"""
class Product:
"""产品类"""
def __init__(self, name, price):
self.name = name
self.price = price
def __repr__(self):
return f"Product(name='{self.name}', price={self.price})"导入包
python
# 导入包的各种方式
# 假设上面的 myapp 包结构已存在
# 1. 导入整个包
import myapp
# 这会执行 myapp/__init__.py
# 2. 导入包中的模块
import myapp.config
config = myapp.config.Config()
# 3. 导入子包中的模块
import myapp.utils.string_utils
result = myapp.utils.string_utils.capitalize_words("hello world")
# 4. 使用 from 导入
from myapp.config import Config
config = Config()
# 5. 导入子包
from myapp.utils import string_utils
result = string_utils.capitalize_words("hello world")
# 6. 导入子包中的特定内容
from myapp.utils.string_utils import capitalize_words
result = capitalize_words("hello world")
# 7. 相对导入(在包内部使用)
# 在 myapp/utils/string_utils.py 中:
# from ..config import Config # 导入上级目录的模块
# from . import math_utils # 导入同级目录的模块
# 8. 使用 __all__ 控制导入
from myapp.utils import *
# 只会导入 __all__ 中指定的内容init.py 文件
python
# __init__.py 文件的用法
# mypackage/__init__.py
# 1. 包初始化代码
print("mypackage 包被导入")
# 2. 定义包级别的变量
__version__ = "1.0.0"
__author__ = "Python教程"
# 3. 导入子模块,简化使用
from .module1 import func1
from .module2 import func2
# 4. 定义 __all__ 控制导入行为
__all__ = ["func1", "func2", "Module1"]
# 5. 包级别的类和函数
def package_function():
"""包级别的函数"""
print("这是包级别的函数")
class PackageClass:
"""包级别的类"""
pass
# 6. 条件初始化
import os
if os.getenv("DEBUG"):
print("调试模式已启用")
# 7. 延迟导入(提高性能)
def get_module1():
"""延迟导入 module1"""
from . import module1
return module1标准库模块
常用标准库
python
# 常用标准库模块介绍
# 1. os - 操作系统接口
import os
print("=== os 模块 ===")
print(f"当前目录: {os.getcwd()}")
print(f"环境变量PATH: {os.getenv('PATH', '未设置')[:50]}...")
print(f"目录分隔符: {os.sep}")
# 2. sys - 系统相关功能
import sys
print("\n=== sys 模块 ===")
print(f"Python版本: {sys.version}")
print(f"平台: {sys.platform}")
print(f"命令行参数: {sys.argv}")
# 3. datetime - 日期时间处理
from datetime import datetime, date, timedelta
print("\n=== datetime 模块 ===")
print(f"当前时间: {datetime.now()}")
print(f"今天日期: {date.today()}")
print(f"一周后: {date.today() + timedelta(days=7)}")
# 4. json - JSON处理
import json
print("\n=== json 模块 ===")
data = {"name": "张三", "age": 20}
json_str = json.dumps(data, ensure_ascii=False)
print(f"JSON字符串: {json_str}")
parsed = json.loads(json_str)
print(f"解析后: {parsed}")
# 5. re - 正则表达式
import re
print("\n=== re 模块 ===")
text = "我的电话是 13812345678"
pattern = r"1[3-9]\d{9}"
match = re.search(pattern, text)
if match:
print(f"找到电话号码: {match.group()}")
# 6. collections - 高级数据结构
from collections import Counter, defaultdict, namedtuple
print("\n=== collections 模块 ===")
# Counter 计数器
words = ["apple", "banana", "apple", "orange", "apple"]
word_count = Counter(words)
print(f"单词计数: {word_count}")
# defaultdict 默认字典
dd = defaultdict(list)
dd["fruits"].append("apple")
print(f"默认字典: {dict(dd)}")
# namedtuple 命名元组
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(f"命名元组: {p}, x={p.x}, y={p.y}")
# 7. itertools - 迭代工具
import itertools
print("\n=== itertools 模块 ===")
# 排列
print(f"排列: {list(itertools.permutations([1, 2, 3], 2))}")
# 组合
print(f"组合: {list(itertools.combinations([1, 2, 3], 2))}")
# 无限迭代器
counter = itertools.count(start=1, step=2)
print(f"前5个奇数: {[next(counter) for _ in range(5)]}")
# 8. functools - 函数工具
from functools import reduce, partial, lru_cache
print("\n=== functools 模块 ===")
# reduce 归约
numbers = [1, 2, 3, 4, 5]
total = reduce(lambda x, y: x + y, numbers)
print(f"求和: {total}")
# partial 偏函数
def power(base, exp):
return base ** exp
square = partial(power, exp=2)
print(f"平方: {square(5)}")
# lru_cache 缓存
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(f"斐波那契(20): {fibonacci(20)}")
# 9. pathlib - 路径处理
from pathlib import Path
print("\n=== pathlib 模块 ===")
p = Path(".")
print(f"当前目录: {p.absolute()}")
print(f"Python文件: {list(p.glob('*.py'))[:3]}...")
# 10. random - 随机数
import random
print("\n=== random 模块 ===")
print(f"随机整数: {random.randint(1, 100)}")
print(f"随机选择: {random.choice(['苹果', '香蕉', '橙子'])}")
print(f"打乱列表: {random.sample([1, 2, 3, 4, 5], 3)}")数学相关模块
python
# 数学相关模块
# 1. math - 数学函数
import math
print("=== math 模块 ===")
print(f"π: {math.pi}")
print(f"e: {math.e}")
print(f"平方根: {math.sqrt(16)}")
print(f"向上取整: {math.ceil(3.2)}")
print(f"向下取整: {math.floor(3.8)}")
print(f"阶乘: {math.factorial(5)}")
print(f"三角函数: sin(π/2) = {math.sin(math.pi / 2)}")
# 2. decimal - 高精度小数
from decimal import Decimal, getcontext
print("\n=== decimal 模块 ===")
# 设置精度
getcontext().prec = 4
# 避免浮点数精度问题
a = Decimal("0.1")
b = Decimal("0.2")
print(f"0.1 + 0.2 = {a + b}") # 精确结果
# 对比普通浮点数
print(f"普通浮点: 0.1 + 0.2 = {0.1 + 0.2}")
# 3. fractions - 分数
from fractions import Fraction
print("\n=== fractions 模块 ===")
f1 = Fraction(1, 3)
f2 = Fraction(1, 6)
print(f"1/3 + 1/6 = {f1 + f2}")
print(f"1/3 * 2 = {f1 * 2}")
print(f"小数转分数: {Fraction(0.5)}")
# 4. statistics - 统计函数
import statistics
print("\n=== statistics 模块 ===")
data = [1, 2, 3, 4, 5, 5, 6, 7, 8, 9]
print(f"平均值: {statistics.mean(data)}")
print(f"中位数: {statistics.median(data)}")
print(f"众数: {statistics.mode(data)}")
print(f"标准差: {statistics.stdev(data):.2f}")
print(f"方差: {statistics.variance(data):.2f}")第三方包管理
pip 包管理器
python
# pip 包管理器使用
# 注意:以下命令在命令行中执行,不是 Python 代码
"""
# 安装包
pip install package_name
# 安装指定版本
pip install package_name==1.0.0
# 安装最低版本
pip install package_name>=1.0.0
# 卸载包
pip uninstall package_name
# 更新包
pip install --upgrade package_name
# 列出已安装的包
pip list
# 查看包信息
pip show package_name
# 搜索包
pip search package_name
# 导出依赖
pip freeze > requirements.txt
# 从文件安装依赖
pip install -r requirements.txt
# 安装开发版本
pip install git+https://github.com/user/repo.git
"""requirements.txt
python
# requirements.txt 文件示例
"""
# 基本格式
package_name==1.0.0
another_package>=2.0.0
# 带注释
requests==2.28.0 # HTTP请求库
numpy>=1.20.0 # 数值计算
pandas>=1.3.0 # 数据分析
# 从git安装
git+https://github.com/user/repo.git@main#egg=package_name
# 本地包
./local_package/
# 包含额外依赖
package_name[extra]==1.0.0
"""
# 生成 requirements.txt
# pip freeze > requirements.txt
# 更优雅的方式:使用 pipreqs
# pip install pipreqs
# pipreqs ./虚拟环境
python
# 虚拟环境管理
# 注意:以下命令在命令行中执行
"""
# 1. venv (Python内置)
# 创建虚拟环境
python -m venv myenv
# 激活虚拟环境
# Windows:
myenv\\Scripts\\activate
# Linux/macOS:
source myenv/bin/activate
# 退出虚拟环境
deactivate
# 2. virtualenv (第三方工具)
# 安装
pip install virtualenv
# 创建虚拟环境
virtualenv myenv
# 指定Python版本
virtualenv -p python3.9 myenv
# 3. conda (Anaconda)
# 创建环境
conda create -n myenv python=3.9
# 激活环境
conda activate myenv
# 退出环境
conda deactivate
# 列出所有环境
conda env list
# 删除环境
conda env remove -n myenv
# 4. poetry (现代包管理工具)
# 安装
pip install poetry
# 创建新项目
poetry new myproject
# 初始化现有项目
poetry init
# 安装依赖
poetry install
# 添加依赖
poetry add package_name
# 添加开发依赖
poetry add --group dev package_name
"""
# 检查当前是否在虚拟环境中
import sys
def check_virtualenv():
"""检查是否在虚拟环境中"""
in_venv = hasattr(sys, 'real_prefix') or (
hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix
)
if in_venv:
print("当前在虚拟环境中")
print(f"虚拟环境路径: {sys.prefix}")
else:
print("不在虚拟环境中")
print(f"Python路径: {sys.prefix}")
return in_venv
check_virtualenv()创建可发布包
setup.py 配置
python
# setup.py - 传统打包方式
"""
from setuptools import setup, find_packages
setup(
name="mypackage", # 包名
version="1.0.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/user/mypackage", # 项目主页
packages=find_packages(), # 自动查找包
classifiers=[ # 分类信息
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires=">=3.6", # Python版本要求
install_requires=[ # 依赖
"requests>=2.25.0",
"numpy>=1.20.0",
],
extras_require={ # 可选依赖
"dev": [
"pytest>=6.0",
"black>=21.0",
],
},
entry_points={ # 命令行入口
"console_scripts": [
"mycommand=mypackage.cli:main",
],
},
)
"""
# pyproject.toml - 现代打包方式(推荐)
"""
[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "mypackage"
version = "1.0.0"
description = "A short description"
readme = "README.md"
authors = [
{name = "Your Name", email = "your@email.com"}
]
license = {text = "MIT"}
requires-python = ">=3.6"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
]
dependencies = [
"requests>=2.25.0",
"numpy>=1.20.0",
]
[project.optional-dependencies]
dev = [
"pytest>=6.0",
"black>=21.0",
]
[project.scripts]
mycommand = "mypackage.cli:main"
[project.urls]
Homepage = "https://github.com/user/mypackage"
"""项目结构示例
python
# 推荐的项目结构
"""
mypackage/
├── pyproject.toml # 项目配置(推荐)
├── setup.py # 安装配置(可选)
├── README.md # 项目说明
├── LICENSE # 许可证
├── .gitignore # Git忽略文件
├── requirements.txt # 依赖列表
├── requirements-dev.txt # 开发依赖
├── src/ # 源代码目录
│ └── mypackage/
│ ├── __init__.py
│ ├── core.py
│ ├── utils.py
│ └── cli.py
├── tests/ # 测试目录
│ ├── __init__.py
│ ├── test_core.py
│ └── test_utils.py
├── docs/ # 文档目录
│ ├── conf.py
│ └── index.rst
└── examples/ # 示例代码
└── example.py
"""
# 构建和发布命令
"""
# 安装构建工具
pip install build twine
# 构建包
python -m build
# 检查包
twine check dist/*
# 上传到 PyPI
twine upload dist/*
# 上传到测试 PyPI
twine upload --repository testpypi dist/*
"""模块开发最佳实践
代码组织
python
# 模块开发最佳实践
# 1. 使用 docstring 文档
def calculate_average(numbers):
"""计算数字列表的平均值
Args:
numbers: 数字列表或可迭代对象
Returns:
float: 平均值,如果列表为空返回 0
Raises:
TypeError: 如果输入不是可迭代对象
Examples:
>>> calculate_average([1, 2, 3, 4, 5])
3.0
>>> calculate_average([])
0
"""
if not numbers:
return 0
return sum(numbers) / len(numbers)
# 2. 使用类型注解
from typing import List, Optional, Union, Dict, Any
def process_data(
data: List[Dict[str, Any]],
key: str,
default: Optional[str] = None
) -> List[str]:
"""处理数据列表
Args:
data: 字典列表
key: 要提取的键
default: 默认值
Returns:
提取的值列表
"""
return [item.get(key, default) for item in data]
# 3. 使用 __all__ 控制公开接口
__all__ = [
"Calculator",
"calculate_average",
"process_data",
]
# 4. 使用 logging 而不是 print
import logging
logger = logging.getLogger(__name__)
def some_function():
logger.debug("调试信息")
logger.info("普通信息")
logger.warning("警告信息")
logger.error("错误信息")
# 5. 使用异常处理
class MyModuleError(Exception):
"""模块自定义异常"""
pass
def risky_operation():
try:
# 可能失败的操作
pass
except SomeError as e:
logger.error(f"操作失败: {e}")
raise MyModuleError("操作失败") from e测试模块
python
# 测试模块示例
# tests/test_mymodule.py
import unittest
from mymodule import Calculator, calculate_average
class TestCalculator(unittest.TestCase):
"""计算器测试类"""
def setUp(self):
"""测试前准备"""
self.calc = Calculator()
def test_add(self):
"""测试加法"""
self.assertEqual(self.calc.add(1, 2), 3)
self.assertEqual(self.calc.add(-1, 1), 0)
def test_divide_by_zero(self):
"""测试除零异常"""
with self.assertRaises(ValueError):
self.calc.divide(10, 0)
class TestCalculateAverage(unittest.TestCase):
"""平均值函数测试"""
def test_normal_list(self):
"""测试正常列表"""
self.assertEqual(calculate_average([1, 2, 3, 4, 5]), 3.0)
def test_empty_list(self):
"""测试空列表"""
self.assertEqual(calculate_average([]), 0)
# 使用 pytest 的测试示例
"""
import pytest
from mymodule import Calculator
def test_calculator_add():
calc = Calculator()
assert calc.add(1, 2) == 3
def test_calculator_divide_by_zero():
calc = Calculator()
with pytest.raises(ValueError):
calc.divide(10, 0)
@pytest.fixture
def calculator():
return Calculator()
def test_with_fixture(calculator):
assert calculator.add(1, 1) == 2
"""
# 运行测试
if __name__ == "__main__":
unittest.main()小结
本章学习了Python模块和包的完整知识:
模块基础:
- 模块的概念和创建
- 导入模块的多种方式
- 模块搜索路径
包的使用:
- 包的结构和创建
__init__.py文件的作用- 包的导入方式
标准库:
- 常用标准库模块
- 数学相关模块
包管理:
- pip 包管理器
- requirements.txt 文件
- 虚拟环境管理
发布包:
- setup.py 和 pyproject.toml
- 项目结构规范
最佳实践:
- 代码组织
- 文档编写
- 测试模块
掌握模块和包的使用,可以帮助你更好地组织代码,提高代码的可维护性和复用性。
