模块与包|第四部分:函数、模块与代码组织 (Functions, Modules & Code Organization)
欢迎来到代码的“建筑规划”局!在上一章,我们学会了将代码封装成可复用的函数“砖块”。但是,当项目变得庞大时,把成千上万块“砖块”都堆在一个工地上(一个.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 创建与使用自定义模块
现在,让我们亲手创建自己的模块!
-
创建一个名为
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."
-
在同一个目录下,创建另一个文件
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}")
-
运行
main.py
,你将看到my_utils.py
中的代码被成功调用了!
9.3 模块搜索路径与 sys.path
你可能会好奇:当我写 import my_utils
时,Python是怎么知道去哪里找这个文件的?
Python会按照一个特定的顺序搜索“路径列表”,这个列表保存在 sys
模块的 path
变量中。搜索顺序如下:
- 当前目录: 运行主程序所在的目录。这就是为什么我们的
main.py
能找到my_utils.py
。 PYTHONPATH
环境变量: 如果设置了,Python会搜索这个环境变量中包含的目录。- 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.py
和formatting.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.py
和 pip
)
当你写好了一个非常棒的包,想分享给别人,或者在你的其他项目中也能方便地使用,而不是每次都复制粘贴文件夹时,你就需要把它打包成一个可安装的格式。
传统上,这通过一个名为 setup.py
的文件来完成。
-
在
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(), # 自动寻找项目中的所有包 )
-
在命令行中安装你的包:
打开终端,进入my_project
目录,然后运行:pip install .
这个
.
代表“当前目录”。pip
会找到setup.py
文件,并根据它的指示来安装你的my_app
包。
安装后有什么好处?
一旦安装成功,my_app
就会被安装到Python的站点包目录中(sys.path
里的一个路径)。这意味着,你可以在你电脑的任何地方、任何项目中,直接 import my_app
,而不再受限于必须在同一个目录下。你的代码真正实现了“一次编写,到处使用”!
太了不起了!你已经从一个“建筑师”晋升为了“城市规划师”。你现在不仅能建造功能强大的函数“建筑”,更能将它们合理地规划到模块“街区”和包“城区”中,构建出一个宏伟而有序的“代码之城”。
我们已经学会了如何组织过程和功能。但现实世界是由“物体”组成的,比如一个“用户”有它的“名字”和“年龄”(数据),也能执行“登录”和“发帖”(行为)。如何用代码来模拟这些现实世界的物体呢?
下一章,我们将踏入一个全新的、激动人心的编程范式——面向对象编程 (Object-Oriented Programming - OOP)。你将学会创建属于你自己的数据类型(类),将数据和操作它们的方法完美地封装在一起。准备好,迎接编程思想的又一次伟大飞跃!