Files

403 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第 2-1 课:模块与包
## 一、本课目标
完成本课后,你将能够:
1. 说出模块和包分别是什么;
2. 解释为什么要把较长的程序拆分成多个文件;
3. 使用 `import` 导入模块;
4. 使用 `from...import...` 导入指定内容;
5. 创建并使用自己的模块和包;
6. 理解 `if __name__ == "__main__":` 的基本作用。
## 二、前置知识
学习本课前,需要掌握:
- 变量和基本数据类型;
- 列表与字典;
- 条件判断与循环;
- 函数、参数和返回值;
- 知道 Python 代码保存在 `.py` 文件中。
本课是 Python 进阶阶段的第一课,但不会使用复杂语法。
## 三、模块是什么
模块Module通常就是一个保存了 Python 代码的 `.py` 文件。
前面的课程大多把函数和主程序写在同一个文件中。程序越来越大后,这种方式会产生三个问题:
1. 文件太长,不容易阅读;
2. 不同功能混在一起,不容易修改;
3. 已经写好的函数不方便在其他程序中重复使用。
把相关函数放进单独文件,可以让代码职责更清晰。例如:
```text
agent_tools.py 保存 Agent 工具相关函数
module_example.py 组织程序运行流程
```
`agent_tools.py` 就是一个自定义模块。
## 四、使用 `import` 导入模块
导入Import表示让当前文件能够使用另一个模块中的内容。
### 4.1 导入标准库模块
标准库Standard Library是安装 Python 时自带的一组模块,不需要另外下载。本课使用 `random` 模块随机选择列表元素:
```python
import random
tools = ["搜索", "终端", "计算器"]
recommended_tool = random.choice(tools)
print(recommended_tool)
```
这里:
- `import random` 导入整个模块;
- `random.choice()` 表示调用 `random` 模块中的 `choice()` 函数;
- 函数会从列表中随机选择一个元素,因此每次结果可能不同。
### 4.2 导入自己的模块
课程目录中的 `agent_tools.py` 定义了函数和变量。在同一目录的 `module_example.py` 中可以这样使用:
```python
import agent_tools
tool_count = agent_tools.get_tool_count(agent_tools.DEFAULT_TOOLS)
print(tool_count)
```
导入时不写 `.py`,因此使用的是 `import agent_tools`,不是 `import agent_tools.py`
通过 `模块名.名称` 可以清楚看出这个变量或函数来自哪里。
## 五、使用 `from...import...`
如果当前文件只需要模块中的少量内容,可以导入指定名称:
```python
from agent_tools import build_agent_description
description = build_agent_description("代码助手", "gpt-5")
print(description)
```
导入后可以直接写函数名,不再需要添加 `agent_tools.`
两种写法都正确:
```python
import agent_tools
agent_tools.build_agent_description("代码助手", "gpt-5")
```
```python
from agent_tools import build_agent_description
build_agent_description("代码助手", "gpt-5")
```
入门阶段建议优先使用清晰、容易看出来源的写法。不要使用 `from agent_tools import *`,因为星号会一次导入许多名称,容易造成重名和阅读困难。
## 六、导入模块时会发生什么
第一次导入模块时Python 会从上到下执行模块中的代码。函数定义只是创建函数,不会自动调用函数;但直接写在文件最外层的 `print()` 会立即执行。
为了区分“直接运行文件”和“把文件作为模块导入”,可以使用:
```python
if __name__ == "__main__":
main()
```
`__name__` 是 Python 自动提供的特殊变量:
- 直接运行当前文件时,`__name__` 的值是 `"__main__"`
- 当前文件被其他文件导入时,`__name__` 的值是模块名。
因此,可以把模块的测试代码放进 `main()`,再通过入口判断调用:
```python
def main():
print("正在测试模块。")
if __name__ == "__main__":
main()
```
这样直接运行模块时会执行测试,导入模块时不会意外输出测试文字。
## 七、包是什么
Package是用于组织多个模块的文件夹。
本课示例结构如下:
```text
agent_package/
├── __init__.py
└── status_text.py
```
`__init__.py` 用来明确表示这个目录是一个 Python 包。本课暂时不在其中编写功能,只保留说明注释。
从包中的模块导入函数:
```python
from agent_package.status_text import get_status_text
status_text = get_status_text(True)
print(status_text)
```
可以把导入路径从左到右理解为:
```text
agent_package 包 → status_text 模块 → get_status_text 函数
```
## 八、示例文件
本课包含以下示例:
```text
02_python进阶/2_1_模块与包/
├── README.md
├── module_example.py
├── agent_tools.py
├── practice.py
└── agent_package/
├── __init__.py
└── status_text.py
```
- `module_example.py`:完整主程序;
- `agent_tools.py`:自己编写的模块;
- `agent_package/`:自己编写的简单包;
- `practice.py`:课堂练习。
## 九、运行方法
打开 PowerShell进入项目根目录
```powershell
cd F:\PyCharm\PythonLearn
```
运行完整示例:
```powershell
python .\02_python进阶\2_1_模块与包\module_example.py
```
直接运行自定义模块的测试:
```powershell
python .\02_python进阶\2_1_模块与包\agent_tools.py
```
完成练习后运行:
```powershell
python .\02_python进阶\2_1_模块与包\practice.py
```
## 十、运行结果
运行 `module_example.py` 时,会看到类似结果:
```text
一、标准库模块
随机推荐工具:搜索
==============================
二、自定义模块
默认工具:['搜索', '终端']
默认工具数量2
代码助手 使用 gpt-5 模型
==============================
三、自定义包
Agent 状态:启用
```
第一部分使用了随机选择,因此也可能显示“终端”或“计算器”,这不是错误。
运行 `agent_tools.py` 时,预期看到:
```text
正在测试 agent_tools 模块。
默认工具数量2
```
## 十一、关键代码执行顺序
运行 `module_example.py`Python 大致按照以下顺序工作:
1. 执行文件顶部的导入语句;
2. 找到并加载标准库模块 `random`
3. 找到同一目录中的 `agent_tools.py`
4. 找到 `agent_package` 包中的 `status_text.py`
5. 创建当前文件中定义的函数;
6. 执行文件末尾的程序入口判断;
7. 因为当前文件是直接运行的,所以调用 `main()`
8. `main()` 依次调用三个演示函数并输出结果。
导入 `agent_tools.py` 时,它的入口判断不成立,所以不会自动执行 `show_module_test()`
## 十二、常见错误
### 12.1 导入时写了 `.py`
错误写法:
```python
import agent_tools.py
```
正确写法:
```python
import agent_tools
```
模块名不包含文件扩展名 `.py`
### 12.2 文件名与导入名不一致
如果文件名是 `agent_tools.py`,就应使用 `import agent_tools`。少写字母或使用不同名称,会出现:
```text
ModuleNotFoundError: No module named 'agent_tool'
```
中文解释Python 没有找到名为 `agent_tool` 的模块。请检查文件名、导入名和运行目录。
### 12.3 导入整个模块后直接调用函数
如果使用:
```python
import agent_tools
```
就要通过模块名调用:
```python
agent_tools.get_tool_count([])
```
直接写 `get_tool_count([])` 会出现名称未定义错误。
### 12.4 自定义模块名与标准库重名
不要把自己的文件命名为 `random.py`,否则 `import random` 可能导入自己的文件,而不是 Python 标准库模块。
### 12.5 导入模块时出现意外输出
如果测试用的 `print()` 直接写在模块最外层,导入模块时也会执行。把测试代码放进 `main()`,再使用程序入口判断。
### 12.6 包或模块不在预期位置
本课请保持示例目录结构不变,并从项目根目录执行讲义中的命令。目录位置错误会导致 Python 找不到需要导入的内容。
## 十三、课堂练习
打开 `practice.py`,按照注释依次完成五部分:
1. 创建 `calculator.py` 模块;
2. 分别使用两种方式导入加法函数;
3. 为模块添加安全的测试入口;
4. 创建 `text_package` 包;
5. 从包中的模块导入文本处理函数。
建议先独立完成。如果遇到困难,先检查文件名、目录位置和导入语句是否一致。
## 十四、参考答案
请先独立练习,再展开查看。
<details>
<summary>查看参考答案</summary>
`calculator.py`
```python
def add(first, second):
"""返回两个数字相加的结果。"""
return first + second
def main():
"""测试当前模块的加法功能。"""
print("正在测试计算模块。")
if __name__ == "__main__":
main()
```
`text_package/__init__.py`
```python
# 这个文件表示 text_package 是一个 Python 包。
```
`text_package/text_tools.py`
```python
def make_title(text):
"""为文本添加标题装饰。"""
return f"=== {text} ==="
```
`practice.py` 中需要补充的核心代码:
```python
import calculator
from calculator import add
from text_package.text_tools import make_title
result = calculator.add(10, 20)
print(f"计算结果:{result}")
new_result = add(5, 8)
print(f"新的计算结果:{new_result}")
title = make_title("Python 学习")
print(title)
```
</details>
## 十五、本课小结
- 模块通常是一个 `.py` 文件;
- 模块可以拆分功能,并让代码在多个程序中重复使用;
- `import 模块名` 会导入整个模块;
- `from 模块名 import 名称` 会导入指定内容;
- `if __name__ == "__main__":` 可以避免导入模块时自动执行测试或主程序;
- 包是用于组织多个模块的文件夹;
- 可以使用点号表示“包、模块、名称”之间的层级关系。
## 十六、验收标准
完成本课时,应满足以下条件:
- 可以成功运行 `module_example.py`
- 可以解释模块与包的区别;
- 可以使用两种方式导入模块内容;
- 可以创建并导入自己的 `.py` 模块;
- 可以解释入口判断的基本作用;
- 可以创建包含 `__init__.py` 的简单包;
- 可以完成 `practice.py` 中的五部分练习;
- 运行练习时得到 `30``13``=== Python 学习 ===`
- 导入 `calculator` 时不会自动输出模块测试文字。