Appearance
Python 模块和包
在 Python 中,模块和包是组织代码的重要方式。本章节将详细介绍 Python 中的模块和包的概念、创建方法和使用技巧。
模块(Module)
模块是一个包含 Python 定义和语句的文件,文件名就是模块名加上 .py 后缀。
基本概念
- 模块:一个
.py文件就是一个模块 - 模块名:文件名去掉
.py后缀 - 命名空间:每个模块都有自己的命名空间,避免命名冲突
创建模块
创建一个简单的模块 my_module.py:
python
# my_module.py
# 变量
pi = 3.14159
name = "My Module"
# 函数
def add(a, b):
"""计算两个数的和"""
return a + b
def subtract(a, b):
"""计算两个数的差"""
return a - b
# 类
class Calculator:
"""简单的计算器类"""
def multiply(self, a, b):
"""计算两个数的乘积"""
return a * b
def divide(self, a, b):
"""计算两个数的商"""
if b == 0:
raise ValueError("除数不能为零")
return a / b
# 执行代码(只有在直接运行模块时才会执行)
if __name__ == "__main__":
print("直接运行模块")
print(f"pi = {pi}")
print(f"add(1, 2) = {add(1, 2)}")
calc = Calculator()
print(f"calc.multiply(3, 4) = {calc.multiply(3, 4)}")导入模块
有多种方式导入模块:
1. 导入整个模块
python
# 导入整个模块
import my_module
# 使用模块中的变量
print(f"pi = {my_module.pi}")
# 使用模块中的函数
print(f"add(1, 2) = {my_module.add(1, 2)}")
# 使用模块中的类
calc = my_module.Calculator()
print(f"multiply(3, 4) = {calc.multiply(3, 4)}")2. 导入模块中的特定内容
python
# 导入模块中的特定内容
from my_module import pi, add, Calculator
# 使用导入的变量
print(f"pi = {pi}")
# 使用导入的函数
print(f"add(1, 2) = {add(1, 2)}")
# 使用导入的类
calc = Calculator()
print(f"multiply(3, 4) = {calc.multiply(3, 4)}")3. 导入模块中的所有内容
python
# 导入模块中的所有内容
from my_module import *
# 使用导入的变量
print(f"pi = {pi}")
print(f"name = {name}")
# 使用导入的函数
print(f"add(1, 2) = {add(1, 2)}")
print(f"subtract(5, 3) = {subtract(5, 3)}")
# 使用导入的类
calc = Calculator()
print(f"multiply(3, 4) = {calc.multiply(3, 4)}")
print(f"divide(10, 2) = {calc.divide(10, 2)}")4. 导入模块并使用别名
python
# 导入模块并使用别名
import my_module as mm
# 使用别名访问模块内容
print(f"pi = {mm.pi}")
print(f"add(1, 2) = {mm.add(1, 2)}")
calc = mm.Calculator()
print(f"multiply(3, 4) = {calc.multiply(3, 4)}")
# 导入特定内容并使用别名
from my_module import add as addition, subtract as subtraction
print(f"addition(1, 2) = {addition(1, 2)}")
print(f"subtraction(5, 3) = {subtraction(5, 3)}")模块的搜索路径
当导入一个模块时,Python 会按照以下顺序搜索模块:
- 当前目录
- 环境变量
PYTHONPATH中的目录 - Python 标准库目录
- 任何
.pth文件中指定的目录
可以通过 sys.path 查看搜索路径:
python
# 查看模块搜索路径
import sys
print("模块搜索路径:")
for path in sys.path:
print(path)标准库模块
Python 内置了很多标准库模块,这些模块提供了各种功能:
| 模块名 | 功能描述 |
|---|---|
os | 操作系统接口 |
sys | Python 解释器相关 |
math | 数学函数 |
random | 随机数生成 |
datetime | 日期和时间处理 |
json | JSON 数据处理 |
re | 正则表达式 |
collections | 高级数据结构 |
itertools | 迭代器工具 |
functools | 函数工具 |
使用示例
python
# 使用标准库模块
# os 模块
import os
print(f"当前目录:{os.getcwd()}")
print(f"当前操作系统:{os.name}")
# sys 模块
import sys
print(f"Python 版本:{sys.version}")
print(f"命令行参数:{sys.argv}")
# math 模块
import math
print(f"π = {math.pi}")
print(f"sin(π/2) = {math.sin(math.pi/2)}")
print(f"平方根(16) = {math.sqrt(16)}")
# random 模块
import random
print(f"随机整数(1-10):{random.randint(1, 10)}")
print(f"随机浮点数:{random.random()}")
print(f"随机选择:{random.choice(['apple', 'banana', 'cherry'])}")
# datetime 模块
import datetime
now = datetime.datetime.now()
print(f"当前时间:{now}")
print(f"年份:{now.year}")
print(f"月份:{now.month}")
print(f"日期:{now.day}")
# json 模块
import json
data = {"name": "Alice", "age": 30, "city": "New York"}
json_str = json.dumps(data)
print(f"JSON 字符串:{json_str}")
parsed_data = json.loads(json_str)
print(f"解析后的数据:{parsed_data}")
# re 模块
import re
pattern = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
email = "example@example.com"
if re.match(pattern, email):
print(f"{email} 是有效的邮箱地址")
else:
print(f"{email} 不是有效的邮箱地址")包(Package)
包是一个包含多个模块的目录,它必须包含一个 __init__.py 文件。
创建包
创建一个包的步骤:
- 创建一个目录作为包名
- 在该目录中创建一个
__init__.py文件 - 在该目录中创建多个模块文件
示例包结构
my_package/
├── __init__.py
├── module1.py
├── module2.py
└── subpackage/
├── __init__.py
└── module3.py__init__.py 文件
__init__.py 文件可以为空,也可以包含包的初始化代码。
示例 __init__.py 文件:
python
# my_package/__init__.py
# 包的版本
__version__ = "1.0.0"
# 包的描述
__description__ = "我的第一个 Python 包"
# 导入模块中的内容
from .module1 import add, subtract
from .module2 import multiply, divide
# 定义 __all__ 变量,控制 from my_package import * 导入的内容
__all__ = ['add', 'subtract', 'multiply', 'divide']模块文件
module1.py:
python
# my_package/module1.py
def add(a, b):
"""计算两个数的和"""
return a + b
def subtract(a, b):
"""计算两个数的差"""
return a - bmodule2.py:
python
# my_package/module2.py
def multiply(a, b):
"""计算两个数的乘积"""
return a * b
def divide(a, b):
"""计算两个数的商"""
if b == 0:
raise ValueError("除数不能为零")
return a / bsubpackage/__init__.py:
python
# my_package/subpackage/__init__.py
# 导入子包中的模块
from .module3 import power
__all__ = ['power']subpackage/module3.py:
python
# my_package/subpackage/module3.py
def power(base, exponent):
"""计算幂"""
return base ** exponent导入包
有多种方式导入包:
1. 导入整个包
python
# 导入整个包
import my_package
# 使用包中的变量
print(f"包版本:{my_package.__version__}")
print(f"包描述:{my_package.__description__}")
# 使用包中的函数
print(f"add(1, 2) = {my_package.add(1, 2)}")
print(f"multiply(3, 4) = {my_package.multiply(3, 4)}")2. 导入包中的模块
python
# 导入包中的模块
import my_package.module1
import my_package.module2
# 使用模块中的函数
print(f"add(1, 2) = {my_package.module1.add(1, 2)}")
print(f"subtract(5, 3) = {my_package.module1.subtract(5, 3)}")
print(f"multiply(3, 4) = {my_package.module2.multiply(3, 4)}")
print(f"divide(10, 2) = {my_package.module2.divide(10, 2)}")3. 导入包中的特定内容
python
# 导入包中的特定内容
from my_package import add, multiply
from my_package.module1 import subtract
from my_package.module2 import divide
# 使用导入的函数
print(f"add(1, 2) = {add(1, 2)}")
print(f"subtract(5, 3) = {subtract(5, 3)}")
print(f"multiply(3, 4) = {multiply(3, 4)}")
print(f"divide(10, 2) = {divide(10, 2)}")4. 导入子包
python
# 导入子包
import my_package.subpackage
from my_package.subpackage import power
# 使用子包中的函数
print(f"power(2, 3) = {my_package.subpackage.power(2, 3)}")
print(f"power(5, 2) = {power(5, 2)}")5. 使用别名
python
# 使用别名
import my_package as mp
import my_package.module1 as m1
import my_package.subpackage as sp
# 使用别名访问
print(f"add(1, 2) = {mp.add(1, 2)}")
print(f"subtract(5, 3) = {m1.subtract(5, 3)}")
print(f"power(2, 3) = {sp.power(2, 3)}")相对导入
在包内部,可以使用相对导入来导入同一包中的其他模块。
相对导入的符号:
.:当前目录..:父目录...:祖父目录,以此类推
示例
python
# my_package/module4.py
# 相对导入
from .module1 import add
from .module2 import multiply
from .subpackage.module3 import power
# 使用导入的函数
def calculate(a, b):
"""计算 a + b * 2^3"""
return add(a, multiply(b, power(2, 3)))
if __name__ == "__main__":
print(f"calculate(1, 2) = {calculate(1, 2)}")模块和包的最佳实践
1. 模块命名规范
- 模块名应该使用小写字母,单词之间用下划线分隔
- 模块名应该简洁明了,反映模块的功能
- 避免使用与标准库模块同名的模块名
2. 包命名规范
- 包名应该使用小写字母,单词之间用下划线分隔
- 包名应该简洁明了,反映包的功能
- 避免使用与标准库包同名的包名
3. __init__.py 文件
__init__.py文件应该保持简洁- 可以在
__init__.py文件中定义包的版本、作者等元数据 - 可以在
__init__.py文件中导入常用的模块内容,方便用户使用 - 可以定义
__all__变量,控制from package import *导入的内容
4. 导入方式
- 对于标准库模块,应该使用
import module或from module import something - 对于第三方库,应该使用
import module或from module import something - 对于自己的模块,应该使用相对导入(在包内部)或绝对导入
- 避免使用
from module import *,除非是在交互式环境中
5. 模块和包的组织
- 按照功能组织模块和包
- 一个模块应该只包含相关的功能
- 对于大型项目,可以创建多个包和子包
- 使用
setup.py或pyproject.toml文件来管理项目
模块的加载和缓存
当导入一个模块时,Python 会:
- 搜索模块文件
- 编译模块文件(如果需要)
- 执行模块代码
- 创建模块对象
- 将模块对象缓存到
sys.modules中
这样,当再次导入同一模块时,Python 会直接从缓存中获取,而不会重新执行模块代码。
查看已加载的模块
python
# 查看已加载的模块
import sys
print("已加载的模块:")
for module_name in list(sys.modules.keys())[:20]: # 只显示前 20 个
print(module_name)
print(f"\n已加载模块数量:{len(sys.modules)}")重新加载模块
如果修改了模块代码,可以使用 importlib.reload() 重新加载模块:
python
# 重新加载模块
import my_module
import importlib
# 第一次导入
print(f"第一次导入:pi = {my_module.pi}")
# 修改模块代码后,重新加载
importlib.reload(my_module)
print(f"重新加载后:pi = {my_module.pi}")示例:创建一个完整的包
项目结构
calculator/
├── __init__.py
├── arithmetic.py
├── geometry.py
└── utils/
├── __init__.py
└── conversion.py文件内容
calculator/__init__.py:
python
# calculator/__init__.py
__version__ = "1.0.0"
__author__ = "Your Name"
from .arithmetic import add, subtract, multiply, divide
from .geometry import area_of_circle, area_of_rectangle
from .utils import convert_to_float
__all__ = [
'add', 'subtract', 'multiply', 'divide',
'area_of_circle', 'area_of_rectangle',
'convert_to_float'
]calculator/arithmetic.py:
python
# calculator/arithmetic.py
def add(a, b):
"""计算两个数的和"""
return a + b
def subtract(a, b):
"""计算两个数的差"""
return a - b
def multiply(a, b):
"""计算两个数的乘积"""
return a * b
def divide(a, b):
"""计算两个数的商"""
if b == 0:
raise ValueError("除数不能为零")
return a / bcalculator/geometry.py:
python
# calculator/geometry.py
import math
def area_of_circle(radius):
"""计算圆的面积"""
return math.pi * radius ** 2
def area_of_rectangle(length, width):
"""计算矩形的面积"""
return length * widthcalculator/utils/__init__.py:
python
# calculator/utils/__init__.py
from .conversion import convert_to_float
__all__ = ['convert_to_float']calculator/utils/conversion.py:
python
# calculator/utils/conversion.py
def convert_to_float(value):
"""将值转换为浮点数"""
try:
return float(value)
except (ValueError, TypeError):
raise ValueError(f"无法转换为浮点数:{value}")使用包
python
# 使用 calculator 包
import calculator
print(f"包版本:{calculator.__version__}")
print(f"包作者:{calculator.__author__}")
# 使用算术函数
print(f"add(1, 2) = {calculator.add(1, 2)}")
print(f"subtract(5, 3) = {calculator.subtract(5, 3)}")
print(f"multiply(3, 4) = {calculator.multiply(3, 4)}")
print(f"divide(10, 2) = {calculator.divide(10, 2)}")
# 使用几何函数
print(f"area_of_circle(5) = {calculator.area_of_circle(5)}")
print(f"area_of_rectangle(3, 4) = {calculator.area_of_rectangle(3, 4)}")
# 使用工具函数
print(f"convert_to_float('123') = {calculator.convert_to_float('123')}")
print(f"convert_to_float(456) = {calculator.convert_to_float(456)}")总结
模块和包是 Python 中组织代码的重要方式,它们可以:
- 提高代码的可维护性和可读性
- 避免命名冲突
- 方便代码的重用
- 使代码结构更加清晰
本章节介绍了:
- 模块的创建和导入
- 包的创建和导入
- 相对导入和绝对导入
- 模块和包的最佳实践
- 模块的加载和缓存
掌握模块和包的使用,对于编写大型 Python 项目非常重要。