模块与包|第四部分:函数、模块与代码组织 (Functions, Modules & Code Organization)

发布于 2025-09-11 分类: Python
系列文章: Python全方位教程
第 1 部分: Python语言概览|第一部分:Python入门与环境搭建 (Python Foundations) 第 2 部分: 搭建开发环境|第一部分:Python入门与环境搭建 (Python Foundations) 第 3 部分: Python基本语法与规范|第一部分:Python入门与环境搭建 (Python Foundations) 第 4 部分: 数据类型深入解析|第二部分:Python核心数据类型与运算符 (Core Data Types & Operators) 第 5 部分: 运算符大全|第二部分:Python核心数据类型与运算符 (Core Data Types & Operators) 第 6 部分: 条件与循环|第三部分:流程控制与数据结构操作 (Control Flow & Data Structures Manipulation) 第 7 部分: 数据结构高级操作|第三部分:流程控制与数据结构操作 (Control Flow & Data Structures Manipulation) 第 8 部分: 函数|第四部分:函数、模块与代码组织 (Functions, Modules & Code Organization) 第 9 部分: 模块与包|第四部分:函数、模块与代码组织 (Functions, Modules & Code Organization) (当前) 第 10 部分: 类与对象|第五部分:面向对象编程 (Object-Oriented Programming - OOP) 第 11 部分: OOP核心特性|第五部分:面向对象编程 (Object--Oriented Programming - OOP) 第 12 部分: 高级OOP主题|第五部分:面向对象编程 (Object-Oriented Programming - OOP) 第 13 部分: 迭代与生成|第六部分:高级Python编程 (Advanced Python) 第 15 部分: 内存管理与性能|第六部分:高级Python编程 (Advanced Python) 第 16 部分: 文件与目录操作|第七部分:Python标准库精选 (The Standard Library) 第 17 部分: 数据处理与序列化|第七部分:Python标准库精选 (The Standard Library) 第 18 部分: 网络与并发编程|第七部分:Python标准库精选 (The Standard Library) 第 20 部分: 系统交互|第七部分:Python标准库精选 (The Standard Library) 第 21 部分: 数据科学与分析入门|第八部分:Python生态与实战应用 (Ecosystem & Applications) 第 22 部分: Web开发入门|第八部分:Python生态与实战应用 (Ecosystem & Applications) 第 23 部分: GUI编程入门|第八部分:Python生态与实战应用 (Ecosystem & Applications) 第 24 部分: 图像处理入门|第八部分:Python生态与实战应用 (Ecosystem & Applications) 第 25 部分: 自动化脚本|第八部分:Python生态与实战应用 (Ecosystem & Applications) 第 26 部分: Python备忘单 (Cheatsheet)|第九部分:附录与资源 (Appendix & Resources) 第 27 部分: 常见面试题与解答|第九部分:附录与资源 (Appendix & Resources) 第 28 部分: 官方文档与其他学习资源链接|第九部分:附录与资源 (Appendix & Resources)

欢迎来到代码的“建筑规划”局!在上一章,我们学会了将代码封装成可复用的函数“砖块”。但是,当项目变得庞大时,把成千上万块“砖块”都堆在一个工地上(一个.py文件)显然是不现实的。我们需要蓝图和分区来规划我们的“代码城市”。

这就是模块 (Module)包 (Package) 的用武之地。

  • 模块 就像一个工具箱,里面装着功能相近的工具(函数、类、变量)。在Python中,一个.py文件就是一个模块。
  • 则像一个五金店,里面分门别类地放着各种工具箱(模块)。在文件系统上,一个包含__init__.py文件的目录就是一个包。

掌握了模块与包,你就能构建出结构清晰、易于维护、方便协作的大型项目。这是从编写“脚本”到开发“软件工程”的决定性一步。让我们开始规划你的第一座“代码城市”吧!

9.1 什么是模块?(import, from...import)

一个Python文件(例如 my_code.py)就是一个模块,它的名字是 my_code。我们可以使用 import 语句来使用其他模块中的代码。

方式一:import module_name (导入整个工具箱)

这是最推荐的方式。它会导入整个模块,你需要通过 module_name.function_name 的方式来使用其中的函数。这样做的好处是代码清晰,永远不会搞混函数到底来自哪个模块。

# 导入Python内置的数学模块 math
import math

# 使用 math 工具箱里的 pi (圆周率) 和 sqrt (平方根) 工具
pi_value = math.pi
root_of_2 = math.sqrt(2)

print(f"圆周率约等于: {pi_value}")
print(f"2的平方根是: {root_of_2}")

方式二:from module_name import item1, item2... (从工具箱里拿出特定工具)

如果你只想用模块中的几个特定工具,并且想直接使用它们的名字,可以用这种方式。

# 只从 math 模块中导入 pi 和 sqrt
from math import pi, sqrt

# 现在可以直接使用,无需加 "math." 前缀
pi_value = pi
root_of_2 = sqrt(2)

print(f"圆周率约等于: {pi_value}")
print(f"2的平方根是: {root_of_2}")

⚠️ 警告: 这种方式虽然方便,但如果导入的函数名与你自己的代码中的函数名冲突,就会产生命名冲突。例如,from math import * 会导入math模块中所有内容,这是一种应该极力避免的坏习惯!

9.2 创建与使用自定义模块

现在,让我们亲手创建自己的模块!

  1. 创建一个名为 my_utils.py 的文件,这就是我们的“工具箱”模块。在里面写入以下内容:

    # file: my_utils.py
    
    PI = 3.14159
    
    def calculate_area(radius):
        """计算圆的面积"""
        return PI * (radius ** 2)
    
    def greet(name):
        """打印问候语"""
        return f"Hello, {name}! Welcome to our module."
    
  2. 在同一个目录下,创建另一个文件 main.py,这是我们的主程序。

    # file: main.py
    
    # 导入我们自己创建的模块
    import my_utils
    
    # 使用模块中的变量和函数
    print(my_utils.greet("Alice"))
    
    radius = 10
    area = my_utils.calculate_area(radius)
    print(f"半径为 {radius} 的圆,面积是 {area}")
    print(f"我们模块定义的PI是: {my_utils.PI}")
    
  3. 运行 main.py,你将看到 my_utils.py 中的代码被成功调用了!

9.3 模块搜索路径与 sys.path

你可能会好奇:当我写 import my_utils 时,Python是怎么知道去哪里找这个文件的?

Python会按照一个特定的顺序搜索“路径列表”,这个列表保存在 sys 模块的 path 变量中。搜索顺序如下:

  1. 当前目录: 运行主程序所在的目录。这就是为什么我们的 main.py 能找到 my_utils.py
  2. PYTHONPATH 环境变量: 如果设置了,Python会搜索这个环境变量中包含的目录。
  3. Python安装目录: Python自带的标准库都在这里。

我们可以通过代码查看这个搜索路径:

import sys

# sys.path 是一个列表,包含了所有Python会去搜索的路径
for path in sys.path:
    print(path)

9.4 __name__ == '__main__' 的作用

这是Python模块中一个极其重要且常见的代码块。要理解它,你首先要知道一个模块的“双重身份”:

  • 它可以被直接运行
  • 它也可以被其他模块导入

Python提供了一个内置的魔法变量 __name__ 来区分这两种情况。

  • 当一个.py文件被直接运行时,它的 __name__ 变量的值是 __main__
  • 当一个.py文件被导入时,它的 __name__ 变量的值是它自己的模块名(文件名)。

这有什么用?
我们可以把一些只希望在“直接运行”时才执行的代码(比如测试代码)放在 if __name__ == '__main__': 这个代码块里。

让我们来改造一下 my_utils.py

# file: my_utils.py (改造后)

PI = 3.14159

def calculate_area(radius):
    """计算圆的面积"""
    return PI * (radius ** 2)

def greet(name):
    """打印问候语"""
    return f"Hello, {name}! Welcome to our module."

# 只有当 my_utils.py 被直接运行时,下面的代码才会执行
# 当它被 main.py 导入时,下面的代码会被忽略
if __name__ == '__main__':
    print("--- 正在对本模块进行自我测试 ---")
    print(greet("Test User"))
    test_area = calculate_area(5)
    print(f"半径为5的测试圆,面积是: {test_area}")
    print("--- 测试结束 ---")

现在:

  • 如果你直接运行 python my_utils.py,你会看到测试输出。
  • 如果你运行 python main.py,你不会看到这些测试输出,因为此时 my_utils 模块的 __name__"my_utils",而不是 "__main__"

这是一种绝佳的实践,让你的模块既可以被复用,又可以独立测试。

9.5 什么是包?(__init__.py 的作用)

当我们的项目越来越大,一个模块(一个文件)也不够用了。我们可能需要把代码按功能划分到不同的目录中。这个目录,如果包含了 __init__.py 文件,就成为了一个包 (Package)

目录结构示例:

my_project/
├── main.py
└── my_app/              <-- 这是一个包
    ├── __init__.py      <-- 标记 my_app 为一个包
    ├── calculations.py  <-- 模块1
    └── formatting.py    <-- 模块2
  • __init__.py:这个文件可以是空的。它的存在告诉Python,my_app 这个目录应该被当作一个包来对待。在更高级的用法中,它还可以用来执行包的初始化代码。
  • calculations.pyformatting.py 是包内的模块。

如何使用包中的模块?

main.py 中,我们可以使用点 . 操作符来导入包里的模块:

# file: main.py

from my_app import calculations
from my_app import formatting

result = calculations.add(10, 20)
formatted_text = formatting.fancy_string("hello")

print(result)
print(formatted_text)

9.6 创建与安装自定义包 (使用 setup.pypip)

当你写好了一个非常棒的包,想分享给别人,或者在你的其他项目中也能方便地使用,而不是每次都复制粘贴文件夹时,你就需要把它打包成一个可安装的格式。

传统上,这通过一个名为 setup.py 的文件来完成。

  1. my_project 目录下(与 my_app 包同级),创建一个 setup.py 文件:

    # file: setup.py
    from setuptools import setup, find_packages
    
    setup(
        name="my_cool_app",         # 包的名称
        version="0.1",             # 版本号
        packages=find_packages(),  # 自动寻找项目中的所有包
    )
    
  2. 在命令行中安装你的包:
    打开终端,进入 my_project 目录,然后运行:

    pip install .
    

    这个 . 代表“当前目录”。pip 会找到 setup.py 文件,并根据它的指示来安装你的 my_app 包。

安装后有什么好处?
一旦安装成功,my_app 就会被安装到Python的站点包目录中(sys.path 里的一个路径)。这意味着,你可以在你电脑的任何地方、任何项目中,直接 import my_app,而不再受限于必须在同一个目录下。你的代码真正实现了“一次编写,到处使用”!


太了不起了!你已经从一个“建筑师”晋升为了“城市规划师”。你现在不仅能建造功能强大的函数“建筑”,更能将它们合理地规划到模块“街区”和包“城区”中,构建出一个宏伟而有序的“代码之城”。

我们已经学会了如何组织过程和功能。但现实世界是由“物体”组成的,比如一个“用户”有它的“名字”和“年龄”(数据),也能执行“登录”和“发帖”(行为)。如何用代码来模拟这些现实世界的物体呢?

下一章,我们将踏入一个全新的、激动人心的编程范式——面向对象编程 (Object-Oriented Programming - OOP)。你将学会创建属于你自己的数据类型(类),将数据和操作它们的方法完美地封装在一起。准备好,迎接编程思想的又一次伟大飞跃!


-- 感谢阅读 --