一、引言:为什么需要模块与包?
随着项目规模的增长,将所有代码写在一个文件中会变得难以维护。Python 提供了 模块(Module) 和 包(Package) 的机制来帮助我们:
- 组织代码:将功能相关的代码分组。
- 提高可读性:结构清晰,易于理解。
- 促进复用:一个模块可以在多个项目中被导入使用。
- 避免命名冲突:通过命名空间隔离不同模块中的同名函数或变量。
二、模块(Module)
1. 什么是模块?
一个 .py
文件就是一个模块,文件名就是模块名。模块中可以包含函数、类、变量等。
2. 创建模块
假设我们创建一个名为 math_utils.py
的文件:
# math_utils.py
def add(a, b):
"""返回两个数的和"""
return a + b
def multiply(a, b):
"""返回两个数的积"""
return a * b
PI = 3.14159
3. 导入模块
在另一个 Python 文件中,可以使用 import
语句导入该模块。
3.1 完整导入
import math_utils
result = math_utils.add(3, 5)
print(result) # 输出:8
print(math_utils.PI) # 输出:3.14159
3.2 导入特定成员
from math_utils import add, multiply
print(add(2, 3)) # 输出:5
print(multiply(4, 5)) # 输出:20
3.3 导入所有成员(不推荐)
from math_utils import *
print(add(1, 2))
print(PI)
⚠️ 注意:from module import *
会污染当前命名空间,容易引发命名冲突,不推荐使用。
3.4 给模块起别名
import math_utils as mu
print(mu.add(10, 20)) # 输出:30
三、包(Package)
1. 什么是包?
包是包含多个模块的目录,用于组织相关模块。它使代码结构更加清晰。
2. 创建包
创建一个名为 my_package
的目录,并在其内部添加一个特殊的文件 __init__.py
(可以为空,也可以包含初始化代码)。
目录结构如下:
my_package/
├── __init__.py
├── calculator.py
└── helper.py
示例文件内容:
# my_package/calculator.py
def calculate_area(radius):
from .helper import PI
return PI * radius ** 2
# my_package/helper.py
PI = 3.14159
def greet():
return "Hello from helper!"
# my_package/__init__.py
# 可以在这里定义包的公共接口
from .calculator import calculate_area
from .helper import PI, greet
__all__ = ['calculate_area', 'PI', 'greet'] # 控制 from package import * 的行为
3. 使用包
# 方式一:导入整个包
import my_package
area = my_package.calculate_area(5)
print(area) # 输出:78.53975
# 方式二:从包中导入特定模块
from my_package import helper
print(helper.greet()) # 输出:Hello from helper!
# 方式三:导入包中的具体函数或变量
from my_package.calculator import calculate_area
print(calculate_area(3)) # 输出:28.27431
四、__init__.py
文件的作用
- 标识一个目录为 Python 包。
- 可以包含包的初始化代码。
- 可以定义哪些模块或函数对外公开(通过
__all__
变量)。 - 可以简化导入路径(例如,在
__init__.py
中导入子模块,使用户可以直接从包名导入)。
五、相对导入 vs 绝对导入
1. 绝对导入
从项目根目录开始指定完整路径。
# 在 my_package/calculator.py 中
from my_package.helper import PI
2. 相对导入
使用 .
或 ..
表示当前包或上级包。
# 在 my_package/calculator.py 中
from .helper import PI # . 表示当前包
✅ 推荐:相对导入更适合包内部模块之间的引用,使代码更具可移植性。
六、标准库与第三方库
1. 标准库
Python 自带的模块集合,无需安装即可使用。
import os
import sys
import json
import datetime
2. 第三方库
通过 pip
安装的外部库。
pip install requests
import requests
response = requests.get("https://httpbin.org/get")
print(response.status_code)
七、模块搜索路径
Python 解释器在导入模块时会按照以下顺序查找:
- 当前目录
- 环境变量
PYTHONPATH
指定的路径 - Python 安装路径下的标准库和第三方库目录
可以通过 sys.path
查看当前的搜索路径:
import sys
for path in sys.path:
print(path)
八、常用内置模块示例
1. os
模块:操作系统接口
import os
print(os.getcwd()) # 获取当前工作目录
os.makedirs("new_dir", exist_ok=True) # 创建目录
2. json
模块:JSON 数据处理
import json
data = {"name": "Alice", "age": 25}
json_str = json.dumps(data)
print(json_str) # {"name": "Alice", "age": 25}
parsed = json.loads(json_str)
print(parsed["name"]) # Alice
3. datetime
模块:日期时间处理
from datetime import datetime
now = datetime.now()
print(now.strftime("%Y-%m-%d %H:%M:%S"))
九、总结:模块与包的核心要点
概念 |
说明 |
模块 |
一个 |
包 |
包含 |
导入方式 |
|
相对导入 |
使用 |
标准库 |
Python 内置模块,开箱即用 |
第三方库 |
通过 |
学习建议:
- 多实践:尝试将你的代码拆分成多个模块和包。
- 理解命名空间:避免命名冲突,合理使用导入方式。
- 熟悉常用标准库:掌握
os
,sys
,json
,datetime
等常用模块。
📌 动手练习:
- 创建一个名为
utils
的包,包含string_utils.py
和number_utils.py
两个模块,并实现一些常用函数。 - 在
__init__.py
中导出这些函数,使得用户可以直接从utils
包导入。 - 使用相对导入在模块之间共享常量。
# 示例:string_utils.py
def reverse_string(s):
return s[::-1]
def to_upper(s):
return s.upper()
# 示例:number_utils.py
def is_even(n):
return n % 2 == 0
def square(n):
return n ** 2
# 示例:__init__.py
from .string_utils import reverse_string, to_upper
from .number_utils import is_even, square
__all__ = ['reverse_string', 'to_upper', 'is_even', 'square']
# 使用示例
from utils import is_even, reverse_string
print(is_even(4)) # True
print(reverse_string("abc")) # cba