1
2
3
4
5
6
7
作者:李晓辉

联系方式:

1. 微信:Lxh_Chat

2. 邮箱:939958092@qq.com
  1. Python 系列(一) 从零开始认识 Python,入门其实没那么难
  2. Python 系列(二) 学 Python 的第一步,把开发环境搭建好
  3. Python 系列(三) 基础语法避坑指南,缩进、大小写、续行与注释
  4. Python 系列(四) Python 输入输出详解,print()、input() 与常见避坑指南
  5. Python 系列(五) 数值类型与运算符详解,整除、浮点精度与进制转换实战
  6. Python 系列(六) 字符串处理实战,查找、替换、切片与日志分析
  7. Python 系列(七) 类型转换详解,int()、float()、str() 与金额计算避坑
  8. Python 系列(八) Python 流程控制入门,掌握 if、elif 和 else
  9. Python 系列(九) Python 逻辑运算符详解,and、or、not 一篇搞懂
  10. Python 系列(十) while、for 循环实战,彻底搞懂 range、break 与 continue
  11. Python 系列(十一) list、tuple、set、dict,到底该怎么选?
  12. Python 系列(十二) List 列表实战,搞定数据增删改查与批量处理
  13. Python 系列(十三) Tuple 元组实战,搞懂不可变特性与 enumerate() 遍历
  14. Python 系列(十四) Set 集合实战,搞定数据去重与集合运算
  15. Python 系列(十五) Dict 字典详解,掌握快速查找与键值操作
  16. Python 系列(十六) sort() 和 sorted() 详解,掌握数据排序技巧
  17. Python 系列(十七) 自定义函数入门,让重复代码一次编写多次使用
  18. Python 系列(十八) 函数参数详解,搞懂位置参数、关键字参数和默认参数
  19. Python 系列(十九) 从 *args 到 **kwargs,让函数调用更灵活
  20. Python 系列(二十) 变量作用域是什么?一文搞懂 global 和 nonlocal
  21. Python 系列(二十一) 搞懂变量、对象和引用,理解函数参数传递机制
  22. Python 系列(二十二) map() 和 filter() 详解,轻松实现批量数据处理与筛选
  23. Python 系列(二十三) 嵌套函数与 lambda 表达式详解,搞懂作用域与闭包
  24. Python 系列(二十四) 搞懂递归函数,从基线条件到实际应用
  25. Python 系列(二十五) 聊聊模块与 import,让代码告别重复编写
  26. Python 系列(二十六) Python 模块入门,从 import 到模块复用
  27. Python 系列(二十七) Python dir() 函数详解,快速查看模块中的函数与属性
  28. Python 系列(二十八) 不会用 Python 标准库?教你快速查找模块和函数
  29. Python 系列(二十九) 搞懂 Python sys 模块,让脚本更适合自动化运维
  30. Python 系列(三十) 为什么 0.1 + 0.2 不等于 0.3?彻底搞懂 Decimal
  31. Python 系列(三十一) 搞懂 datetime 日期时间,轻松处理时区与时间计算

前面我们学习了 Python 的变量、数据类型、条件判断、循环以及函数。

随着程序越来越复杂,我们可能会遇到一个问题:所有代码都写在同一个 Python 文件中,会不会越来越难维护?

比如,我们想写一个服务器巡检工具,既要检查磁盘使用率,又要检查内存、CPU 和服务状态。如果把所有功能都放进一个文件,代码很快就会变得臃肿。

有没有办法把不同功能拆分到不同文件中,需要时再把它们组合起来?

当然有,这就是 Python 的模块(Module)。

这篇文章,我们从最基础的 import 开始,逐步理解模块的导入方式、执行顺序、缓存机制和搜索路径,最后用一个简单的服务器磁盘检查项目把这些知识串起来。

为什么需要模块?

先从一个简单的例子开始。

假设我们要编写一个程序,计算服务器磁盘的使用率:

1
2
3
4
5
6
total = 1000
used = 760

usage = used / total * 100

print(f"磁盘使用率:{usage:.1f}%")

运行后,输出如下:

1
磁盘使用率:76.0%

这段代码很简单,但如果后续还要检查内存、CPU 和服务状态,所有代码都堆在一起,就不容易维护了。

我们可以按照功能拆分文件:

1
2
3
4
5
server_check/
├── main.py
├── disk_check.py
├── memory_check.py
└── service_check.py

每个文件负责一项工作:

  • main.py:程序入口,负责组织整个巡检流程。
  • disk_check.py:检查磁盘使用情况。
  • memory_check.py:检查内存使用情况。
  • service_check.py:检查服务运行状态。

这些 .py 文件就可以作为 Python 模块使用。

简单理解:一个 Python 模块通常就是一个 .py 文件。 我们可以把相关代码放在不同模块中,再通过导入的方式使用它们。

这样做的好处很直接:代码更容易复用,功能划分更清晰,也方便后续维护和测试。

使用 import 导入模块

先学习最常见的导入方式:import。

Python 自带了许多可以直接使用的功能。例如,math 模块提供了平方根、圆周率等数学功能。

创建第一个示例

为了避免不同示例之间相互覆盖,我们为每个练习单独创建一个目录。

先创建目录:

1
2
mkdir -p python_module_demo/01_basic
cd python_module_demo/01_basic

在这个目录下创建 math_demo.py,写入以下代码:

1
2
3
4
5
6
import math

result = math.sqrt(16)

print(result)
print(math.pi)

这里有几个值得注意的地方。

import math 表示导入 math 模块。

math.sqrt(16) 表示调用 math 模块中的 sqrt() 函数,计算 16 的平方根。

math.pi 表示访问 math 模块中的 pi 属性,也就是圆周率。

注意,导入模块后,访问其中的函数或属性时,需要使用模块名加点号的形式。

运行程序

在当前目录中执行:

1
python math_demo.py

输出类似:

1
2
4.0
3.141592653589793

为什么结果是 4.0,而不是 4?

因为 math.sqrt() 返回的是浮点数。即使平方根恰好是整数,结果仍然以浮点数的形式表示。

这里的 python math_demo.py 表示使用 Python 解释器执行当前目录下的 math_demo.py 文件。

如果系统使用的是 python3 命令,也可以执行:

1
python3 math_demo.py

import 的基本语法

1
import 模块名

导入后,可以通过下面的方式访问模块中的内容:

1
2
模块名.函数名()
模块名.属性名

例如:

1
2
3
4
import math

print(math.sqrt(25))
print(math.pi)

这种写法的优点是清晰:看到 math.sqrt(),就知道这个函数来自 math 模块。

使用 from … import 导入指定内容

刚才我们导入了整个 math 模块,再通过 math.sqrt() 调用函数。

如果只需要其中的 sqrt() 函数,能不能直接导入它?

可以,这时就可以使用 from ... import ... 语法。

创建独立示例

创建另一个目录:

1
2
mkdir -p ../02_from_import
cd ../02_from_import

创建 sqrt_demo.py,写入以下代码:

1
2
3
4
from math import sqrt, pi

print(sqrt(16))
print(pi)

这里的含义是:

  • from math:指定从哪个模块导入。
  • import sqrt, pi:指定要导入的函数和属性。

导入之后,可以直接使用 sqrt() 和 pi,不必再写 math.sqrt() 和 math.pi。

运行程序:

1
python sqrt_demo.py

输出:

1
2
4.0
3.141592653589793

两种导入方式有什么区别?

导入方式调用方式特点
import mathmath.sqrt(16)来源清晰,适合导入模块后使用多个功能
from math import sqrtsqrt(16)调用简洁,适合明确使用少量功能

这两种方式都很常见,可以根据实际需求选择。

需要注意的是,from math import sqrt 并不意味着 Python 只加载了 sqrt() 这一个独立文件。它仍然需要导入对应的 math 模块,只是将指定的名称直接绑定到当前命名空间中。

初学阶段,可以优先使用 import 模块名。当需要使用的内容比较明确时,再使用 from ... import ...。

使用 as 为模块起别名

有时,模块名称比较长,或者我们希望在代码中使用更简短的名称,就可以使用 as 设置别名。

创建目录:

1
2
mkdir -p ../03_alias
cd ../03_alias

创建 alias_demo.py:

1
2
3
4
import math as m

print(m.sqrt(36))
print(m.pi)

运行:

1
python alias_demo.py

输出:

1
2
6.0
3.141592653589793

这里的 as m 表示将导入的 math 模块绑定为名称 m。后续就可以使用 m.sqrt() 和 m.pi。

同样,from ... import ... 也可以设置别名:

1
2
3
from math import sqrt as square_root

print(square_root(49))

输出:

1
7.0

别名只是当前代码中的另一个名称,并不会改变模块本身。

实际开发中,很多常用库都有约定俗成的别名。例如:

1
2
import numpy as np
import pandas as pd

不过,刚开始学习时,不必为了缩短代码而给所有模块起别名。名称清晰通常比名称简短更重要。

Python 模块有哪些类型?

前面使用的 math 是 Python 自带的模块。但在查阅资料时,你可能还会看到“内置模块”“标准库模块”和“第三方模块”等说法。

这些概念有关联,但不能完全画等号。

内置模块

内置模块(Built-in Module) 是指由当前 Python 解释器以内置方式提供的模块。具体有哪些模块,与 Python 的实现和构建方式有关。

例如,在常见的 CPython 环境中,sys 就是内置模块。

它提供了与 Python 解释器运行环境相关的功能,例如查看解释器版本、访问模块搜索路径等。

1
2
3
4
import sys

print(sys.version)
print(sys.builtin_module_names)

其中:

  • sys.version:查看当前 Python 解释器的版本信息。
  • sys.builtin_module_names:查看当前解释器中以内置模块形式提供的模块名称。
1
2
3.15.0 (tags/v3.15.0:d220409, Oct  9 2026, 11:55:56) [MSC v.1951 64 bit (AMD64)]
('_abc', '_ast', '_bisect', '_blake2', '_codecs', '_codecs_cn', '_codecs_hk', '_codecs_iso2022', '_codecs_jp', '_codecs_kr', '_codecs_tw', '_collections', '_contextvars', '_csv', '_datetime', '_functools', '_heapq', '_hmac', '_imp', '_interpchannels', '_interpqueues', '_interpreters', '_io', '_json', '_locale', '_lsprof', '_math_integer', '_md5', '_multibytecodec', '_opcode', '_operator', '_pickle', '_random', '_sha1', '_sha2', '_sha3', '_signal', '_sre', '_stat', '_statistics', '_string', '_struct', '_suggestions', '_symtable', '_sysconfig', '_thread', '_tokenize', '_tracemalloc', '_types', '_typing', '_warnings', '_weakref', '_winapi', 'array', 'atexit', 'binascii', 'builtins', 'cmath', 'errno', 'faulthandler', 'gc', 'itertools', 'marshal', 'math', 'mmap', 'msvcrt', 'nt', 'sys', 'time', 'winreg', 'xxsubtype', 'zlib')

在常见的 CPython 环境中,列表中会包含 sys。不同 Python 实现或构建环境的结果可能不同。

Python 标准库模块

标准库(Standard Library) 是 Python 随附的一整套模块和包,提供文件操作、路径处理、数学计算、日期时间处理等常用功能。

标准库的范围比内置模块更广,其中既包括内置模块,也包括通过其他方式实现的模块。

例如:

  • sys:解释器运行环境相关功能,在常见 CPython 中属于内置模块。
  • math:数学运算功能,在常见 CPython 中通常是标准库中的扩展模块。
  • pathlib:面向对象的路径处理功能。
  • shutil:文件和目录的高级操作。

这些标准库模块通常不需要使用 pip 单独安装,安装好 Python 后就可以使用。

例如,使用 pathlib 查看当前目录:

1
2
3
from pathlib import Path

print(Path.cwd())

输出会根据你运行程序时所在的目录而变化。

第三方模块和包

除了 Python 自带的标准库,开发者还可以安装第三方库,为程序增加更多功能。

例如:

  • requests:常用于 HTTP 请求。
  • numpy:常用于数值计算。
  • pandas:常用于数据处理。

以 requests 为例,在具备网络连接的环境中,可以使用以下命令安装:

1
python -m pip install requests

然后在程序中导入:

1
import requests

注意:这里的 requests 是第三方库,并不是 Python 默认自带的标准库。

在企业环境中,安装第三方依赖前,还需要考虑网络访问、软件源、版本兼容和安全管理等问题。

简单归纳

类型含义示例
内置模块由解释器以内置方式提供的模块常见 CPython 中的 sys
标准库模块Python 随附的模块和包,范围更广math、pathlib、shutil
第三方模块或包由外部项目提供,通常需要额外安装requests、numpy

这里要记住:内置模块是更具体的分类,标准库的范围更广;第三方库则来自 Python 自带标准库之外的项目。 不要把所有标准库模块都称为内置模块。

模块导入时,代码会执行吗?

这是学习模块时非常重要的一点。

我们通常认为,导入模块只是让程序能够使用其中的函数。但实际上,模块第一次被导入时,模块顶层的代码也会执行。

先通过一个实验观察。

创建模块文件

创建目录:

1
2
mkdir -p ../04_import_order
cd ../04_import_order

创建 order_demo.py:

1
2
3
4
5
6
7
8
print("正在加载 order_demo 模块")

server_name = "web01"

def check_server():
print(f"正在检查服务器:{server_name}")

print("模块顶层代码执行完毕")

输出

1
2
正在加载 order_demo 模块
模块顶层代码执行完毕

这个文件包含三部分内容:

  • 两条位于顶层的 print() 语句。
  • 一个变量 server_name。
  • 一个函数 check_server()。

注意,def check_server(): 只是定义函数,并不会立即执行函数体中的 print()。

创建调用文件

在同一个目录下创建 main.py:

1
2
3
4
5
6
7
8
9
print("程序开始")

import order_demo

print("模块导入完成")

order_demo.check_server()

print("程序结束")

现在目录结构如下:

1
2
3
04_import_order/
├── main.py
└── order_demo.py

运行:

1
python main.py

输出:

1
2
3
4
5
6
程序开始
正在加载 order_demo 模块
模块顶层代码执行完毕
模块导入完成
正在检查服务器:web01
程序结束

我们逐行分析执行顺序。

  1. Python 从 main.py 开始执行,因此先输出“程序开始”。
  2. 执行 import order_demo,Python 查找并加载 order_demo 模块。
  3. 模块首次导入时,order_demo.py 中的顶层代码按顺序执行,因此输出两条提示信息。
  4. 函数 check_server() 被定义,但此时函数体还没有执行。
  5. 模块导入完成,程序继续执行 main.py 后面的代码。
  6. 调用 order_demo.check_server(),这时才执行函数体。
  7. 最后输出“程序结束”。

可以把整个过程理解为:

flowchart LR
    A["运行 main.py"] --> B["执行程序开始"]
    B --> C["导入 order_demo"]
    C --> D["执行模块顶层代码"]
    D --> E["完成模块导入"]
    E --> F["继续执行 main.py"]
    F --> G["调用 check_server()"]
    G --> H["执行函数体"]
    H --> I["程序结束"]

这也解释了为什么不应该随意在模块顶层执行有明显副作用的操作。例如,一导入模块就重启服务、删除文件或者执行大规模巡检,可能会让其他程序在导入时意外触发这些操作。

为什么常见 Python 文件都有 if name == “main“?

在 Python 代码中,你经常会看到下面这种写法:

1
2
if __name__ == "__main__":
main()

这段代码有什么用?

我们继续在 04_import_order 目录中创建一个新文件 entry_demo.py:

1
2
3
4
5
def main():
print("开始执行主程序")

if __name__ == "__main__":
main()

当我们直接运行这个文件:

1
python entry_demo.py

输出:

1
开始执行主程序

这是因为直接运行文件时,Python 会将该模块的 __name__ 设置为 "__main__",因此条件成立,main() 被调用。

如果另一个文件导入 entry_demo:

1
2
3
import entry_demo

print("导入完成")

保存为同目录下的 import_entry_demo.py,执行:

1
python import_entry_demo.py

输出:

1
导入完成

此时,entry_demo 是被导入的模块,其 __name__ 通常是 "entry_demo",而不是 "__main__",所以条件不成立,main() 不会因为导入而自动执行。

这是一种很常见的代码组织方式:把可复用的函数定义放在模块中,把只应该在直接运行文件时执行的逻辑放进 if __name__ == "__main__": 中。

为什么重复 import 通常不会重复执行模块代码?

假设我们在同一个程序里,多次导入同一个模块:

1
2
3
import order_demo
import order_demo
import order_demo

是不是会把 order_demo.py 的代码执行三遍?

通常不会。

Python 会在导入过程中缓存已经加载的模块。模块通常会被记录在 sys.modules 中;后续在同一个解释器进程中再次导入该模块时,Python 会优先复用已经缓存的模块对象,而不是重新执行整个模块。

验证模块缓存

在 04_import_order 目录下创建 cache_demo.py:

1
print("cache_demo 模块正在执行")

再创建 test_cache.py:

1
2
3
4
5
import cache_demo
import cache_demo
import cache_demo

print("导入操作完成")

运行:

1
python test_cache.py

输出:

1
2
cache_demo 模块正在执行
导入操作完成

虽然写了三次 import cache_demo,但模块顶层的提示信息只出现一次。

原因是:第一次导入时,模块被加载并执行;后续导入则复用缓存中的模块对象。

注意,这里的“一次”是指同一个 Python 解释器进程中,在模块没有被重新加载等特殊操作的情况下。如果重新启动 Python 程序,就会创建新的进程,模块缓存也会重新建立。

sys.modules 是什么?

sys.modules 是 Python 用来记录已导入模块的字典。

我们可以通过下面的代码检查模块是否已经加载:

1
2
3
4
import sys
import math

print("math" in sys.modules)

输出通常是:

1
True

这是因为前面已经导入了 math,当前解释器中存在对应的模块缓存条目。

模块导入的简化流程如下:

flowchart TD
    A["执行 import 模块名"] --> B{"模块是否已在 sys.modules 中?"}
    B -->|是| C["复用已缓存的模块对象"]
    B -->|否| D["查找并创建模块对象"]
    D --> E["将模块加入 sys.modules"]
    E --> F["执行模块代码"]
    C --> G["完成导入"]
    F --> G

这是一种便于理解的简化流程。实际导入过程还涉及模块查找器、加载器、包结构等机制;尤其是模块初始化时,Python 通常会在执行模块代码之前就将模块放入 sys.modules,这也有助于处理循环导入等情况。

如果确实需要重新执行模块怎么办?

Python 提供了 importlib.reload(),可以重新加载已经导入的模块。

例如:

1
2
3
4
import importlib
import cache_demo

importlib.reload(cache_demo)

如果 cache_demo.py 中有顶层的 print(),重新加载时通常会再次看到该提示信息。

不过,重新加载模块并不意味着程序里所有已经引用的对象都会自动更新。例如,之前通过 from cache_demo import some_function 导入的名称,不会因为重新加载模块就自动重新绑定到新的函数对象。

因此,日常开发中一般不需要手动重新加载模块。修改代码后,重新运行程序往往更简单,也更容易理解。

Python 到哪里查找模块?

前面所有示例都把相关文件放在同一个目录中,所以导入过程比较顺利。

但如果模块放在其他目录,Python 是如何找到它的?

这就涉及模块搜索路径。

查看 sys.path

创建目录:

1
2
mkdir -p ../06_module_path
cd ../06_module_path

创建 show_path.py:

1
2
3
4
import sys

for path in sys.path:
print(path)

运行:

1
python show_path.py

输出会根据你的 Python 安装方式和运行环境变化,可能包含:

1
2
3
当前脚本所在目录
Python 标准库目录
第三方库安装目录

实际输出通常是多个文件系统路径,不一定与上面的描述完全相同。

sys.path 是一个列表,列出了 Python 在查找模块时使用的搜索路径。具体搜索行为还会受到导入机制、包结构和模块加载器等因素影响。

初学阶段可以先记住:当 Python 找不到要导入的模块时,首先要检查模块文件是否放在合适的位置,以及当前程序的搜索路径是否包含该位置。

使用包组织多个模块

当项目只有一两个文件时,直接使用模块通常就够了。

随着项目增大,我们可以把多个相关模块组织成一个包(Package)。

例如,创建以下目录结构:

1
2
3
4
5
06_module_path/
├── main.py
└── tools/
├── __init__.py
└── math_demo.py

这里:

  • main.py 是调用程序。
  • tools/ 是存放相关功能模块的目录。
  • __init__.py 可以让 tools 以传统的 Python 包形式组织起来。
  • math_demo.py 是包中的一个模块。

创建 tools/math_demo.py:

1
2
def add(a, b):
return a + b

tools/__init__.py 可以先留空。

然后在 main.py 中写入:

1
2
3
from tools.math_demo import add

print(add(10, 20))

在 06_module_path 目录下运行:

1
python main.py

输出:

1
30

Python 找到了 tools 包中的 math_demo 模块,并导入其中的 add() 函数。

这里的 tools.math_demo 可以理解为从包到模块的层级路径。

需要注意,现代 Python 也支持不包含 __init__.py 的命名空间包,因此 __init__.py 并非所有包结构都必须具备。不过,对于初学者和很多常规项目来说,保留这个文件有助于清楚地表达包的结构。

实战:把服务器磁盘检查功能拆分成模块

前面学习了 import、模块执行顺序、缓存和包结构。接下来,把这些知识用到一个简单的 Linux 运维场景中。

假设我们希望编写一个程序,检查 Linux 服务器的根文件系统使用率,并在使用率达到阈值时输出告警。

这里先使用 Python 标准库中的 shutil 获取磁盘空间信息,不依赖第三方模块。

创建项目目录

在终端中执行:

1
2
mkdir -p 07_server_project/server_check
cd 07_server_project

最终的项目结构如下:

1
2
3
4
5
07_server_project/
└── server_check/
├── __init__.py
├── main.py
└── disk_check.py

三个文件分别负责:

  • __init__.py:标识并组织包,可以先留空。
  • disk_check.py:实现磁盘检查功能。
  • main.py:程序入口,负责调用检查功能。

编写磁盘检查模块

创建 server_check/disk_check.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import shutil

def check_disk(path="/"):
total, used, free = shutil.disk_usage(path)

usage = used / total * 100

print(f"检查路径:{path}")
print(f"磁盘总容量:{total / (1024 ** 3):.2f} GiB")
print(f"已使用容量:{used / (1024 ** 3):.2f} GiB")
print(f"可用容量:{free / (1024 ** 3):.2f} GiB")
print(f"磁盘使用率:{usage:.1f}%")

if usage >= 80:
print("告警:磁盘使用率达到或超过 80%!")
else:
print("状态正常:磁盘使用率低于 80%。")

这段代码的核心是:

1
total, used, free = shutil.disk_usage(path)

shutil.disk_usage() 会返回指定路径所在文件系统的总容量、已使用容量和可用容量。

这里使用 / 作为默认路径,表示检查根文件系统。容量除以 1024 ** 3 后,以 GiB 为单位显示。

然后通过:

1
usage = used / total * 100

计算已使用容量占总容量的百分比。

最后用 if 判断是否达到 80% 的告警阈值。这个阈值只是演示用的示例,实际生产环境应根据业务和文件系统的使用情况设置。

编写程序入口

创建 server_check/main.py:

1
2
3
4
5
6
7
from .disk_check import check_disk

def main():
check_disk("/")

if __name__ == "__main__":
main()

这里使用了:

1
from .disk_check import check_disk

开头的点号表示从当前包中导入 disk_check 模块里的 check_disk() 函数。

由于使用了相对导入,我们通过包的模块路径启动程序,而不是直接把 main.py 当成普通脚本运行。

运行程序

确认当前目录是 07_server_project,执行:

1
python -m server_check.main

如果系统使用 python3,则执行:

1
python3 -m server_check.main

输出示例:

1
2
3
4
5
6
检查路径:/
磁盘总容量:50.00 GiB
已使用容量:21.50 GiB
可用容量:28.50 GiB
磁盘使用率:43.0%
状态正常:磁盘使用率低于 80%。

注意,上面的容量和使用率只是示例,实际输出取决于运行程序的 Linux 服务器。

这里的 -m 表示让 Python 按模块的方式查找并执行 server_check.main。因为我们从项目目录启动,Python 可以识别 server_check 包及其内部模块,相对导入也就能够正常工作。

整个项目的调用关系可以概括为:

flowchart TD
    A["执行 python -m server_check.main"] --> B["加载 server_check.main"]
    B --> C["导入 disk_check 模块"]
    C --> D["定义 check_disk() 函数"]
    D --> E["执行 main()"]
    E --> F["调用 check_disk('/')"]
    F --> G["获取文件系统容量"]
    G --> H["计算磁盘使用率"]
    H --> I{"使用率是否达到 80%?"}
    I -->|是| J["输出告警"]
    I -->|否| K["输出正常状态"]

这个例子虽然简单,但已经体现了实际项目中常见的结构:功能模块负责具体工作,入口模块负责组织调用。

后续如果要继续增加内存检查、CPU 检查和服务状态检查,只需分别创建相应模块,再由 main.py 组织执行即可。

常见问题

为什么提示 No module named xxx?

这通常表示 Python 没有找到要导入的模块。可以检查:

  • 模块文件是否存在,文件名是否拼写正确。
  • 程序启动时所在目录是否符合预期。
  • 模块所在目录是否在 Python 的搜索路径中。
  • 如果是第三方库,当前 Python 环境中是否已经安装。

如果机器上安装了多个 Python 版本,还要确认安装依赖和运行程序时使用的是同一个解释器。

为什么模块导入时会输出内容?

因为模块首次加载时,顶层代码会执行。

如果不希望某段逻辑在导入时自动运行,可以把它放进函数中,并在适当的位置调用;对于程序入口逻辑,也可以使用 if __name__ == "__main__":。

为什么重复导入没有重复执行?

因为 Python 通常会使用 sys.modules 中缓存的模块对象。同一个解释器进程中,后续导入一般会复用已有模块,而不是重新执行整个模块。

模块、包和第三方库有什么区别?

  • 模块:通常指一个 Python 文件。
  • 包:用于组织多个模块的结构。
  • 第三方库:由 Python 标准库之外的项目提供的软件组件,可能包含一个或多个包和模块。

这些概念并不完全处于同一分类层级,但理解它们的区别,有助于阅读项目目录和安装说明。

写在最后

到这里,我们已经掌握了 Python 模块的基本使用方法:

  • 使用 import 导入模块。
  • 使用 from ... import ... 导入指定名称。
  • 使用 as 设置别名。
  • 区分内置模块、标准库模块和第三方模块。
  • 理解模块首次导入时的代码执行顺序。
  • 认识 sys.modules 以及模块缓存机制。
  • 使用 sys.path 理解模块搜索路径。
  • 使用包组织代码,并在实际项目中拆分功能模块。

学习模块,不只是记住几种导入语法,更重要的是开始改变编写程序的方式:把不同职责的代码拆分开,让程序更容易阅读、复用和维护。

下一篇,我们继续学习一个实用的内置函数:dir()。它可以帮助我们查看对象或模块中有哪些名称,方便进一步探索 Python 模块提供的功能。