# 第 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. 从包中的模块导入文本处理函数。 建议先独立完成。如果遇到困难,先检查文件名、目录位置和导入语句是否一致。 ## 十四、参考答案 请先独立练习,再展开查看。
查看参考答案 `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) ```
## 十五、本课小结 - 模块通常是一个 `.py` 文件; - 模块可以拆分功能,并让代码在多个程序中重复使用; - `import 模块名` 会导入整个模块; - `from 模块名 import 名称` 会导入指定内容; - `if __name__ == "__main__":` 可以避免导入模块时自动执行测试或主程序; - 包是用于组织多个模块的文件夹; - 可以使用点号表示“包、模块、名称”之间的层级关系。 ## 十六、验收标准 完成本课时,应满足以下条件: - 可以成功运行 `module_example.py`; - 可以解释模块与包的区别; - 可以使用两种方式导入模块内容; - 可以创建并导入自己的 `.py` 模块; - 可以解释入口判断的基本作用; - 可以创建包含 `__init__.py` 的简单包; - 可以完成 `practice.py` 中的五部分练习; - 运行练习时得到 `30`、`13` 和 `=== Python 学习 ===`; - 导入 `calculator` 时不会自动输出模块测试文字。