Skip to content

01-模块基础

Python 3.11+

本章讲解 Python 模块的基本概念、导入语法和模块属性。


概念铺垫

1.1 什么是模块

实际场景

你写了一个计算工具 calculator.py,里面有加法、减法等函数。现在你想在另一个项目 invoice.py 里使用这些函数,不想重复写代码。

问题:如何让多个项目共享同一份代码?

概念说明

概念说明

在 Python 中,一个 .py 文件就是一个模块。

模块的名字就是去掉了 .py 后缀的文件名。模块里面可以包含变量、函数、类,甚至可以包含可执行的代码。

┌─────────────────────────────────────────────────────────────┐
│                    模块 = 文件                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   文件:math_utils.py                                       │
│   模块名:math_utils                                        │
│                                                             │
│   文件:my_package/helpers.py                               │
│   模块名:my_package.helpers                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

模块的作用:

┌─────────────────────────────────────────────────────────────┐
│                    模块的作用                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   1. 代码组织                                               │
│      将相关功能放在同一文件中,便于管理                     │
│                                                             │
│   2. 代码复用                                               │
│      一个模块可以被多个程序导入使用                         │
│                                                             │
│   3. 命名空间隔离                                           │
│      每个模块有独立的命名空间,避免命名冲突                 │
│                                                             │
│   4. 维护方便                                               │
│      修改模块代码,所有导入该模块的程序自动受益             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

模块的类型:

类型说明示例
内置模块Python 内置,无需安装sys, os, math
标准库模块Python 自带,需导入datetime, json, re
第三方模块需要安装requests, numpy
自定义模块用户自己编写my_module.py

模块的执行过程

导入时发生了什么

┌─────────────────────────────────────────────────────────────┐
│              模块导入执行过程                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   1. 搜索模块                                               │
│      在 sys.path 中查找模块文件                             │
│                                                             │
│   2. 编译模块                                               │
│      如果 .pyc 不存在或过期,编译为字节码                   │
│                                                             │
│   3. 执行模块                                               │
│      执行模块中的所有顶层代码                               │
│                                                             │
│   4. 创建模块对象                                           │
│      创建模块对象,存入 sys.modules                         │
│                                                             │
│   5. 绑定名称                                               │
│      将模块名绑定到当前命名空间                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

模块的搜索路径(sys.path)

当你输入 import xxx 时,Python 会按照 sys.path 列表中的路径顺序去查找:

python
import sys
print(sys.path)

搜索顺序:

┌─────────────────────────────────────────────────────────────┐
│                  sys.path 搜索顺序                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   1. 当前执行脚本所在的目录                                 │
│                                                             │
│   2. 环境变量 PYTHONPATH 中指定的目录                       │
│                                                             │
│   3. Python 的标准库安装目录                                │
│                                                             │
│   4. 第三方库目录(通常是 site-packages)                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

模块的缓存机制

为了提高性能,Python 在一次运行中只会导入同一个模块一次

python
# module.py
print("模块被加载")

# main.py
import module  # 输出:模块被加载
import module  # 无输出(模块已缓存)

sys.modules 字典:

python
import sys

# 查看已加载的模块
print(sys.modules.keys())

# 检查模块是否已加载
if 'math' in sys.modules:
    print("math 模块已加载")

# 模块对象存储在 sys.modules 中
import math
print(sys.modules['math'] is math)  # True

L1 理解层:会用

import 语法

基本导入方式

Python 提供多种导入方式,可根据需要选择。

python
# 方式 1:导入整个模块
import math
print(math.sqrt(16))  # 4.0
print(math.pi)        # 3.14159...

# 方式 2:导入模块中的特定对象
from math import sqrt, pi
print(sqrt(16))  # 4.0
print(pi)        # 3.14159...

# 方式 3:导入模块中的所有内容(不推荐)
from math import *
print(sqrt(16))  # 可能造成命名冲突

# 方式 4:使用别名
import math as m
print(m.sqrt(16))  # 4.0

from math import sqrt as square_root
print(square_root(16))  # 4.0

import vs from 的本质区别

用"盒子"比喻理解

┌─────────────────────────────────────────────────────────────┐
│              import math:导入整个"盒子"                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   import math                                               │
│                                                             │
│   你的代码空间                    math 模块(盒子)         │
│   ┌─────────────────┐           ┌─────────────────┐        │
│   │                 │           │ sqrt()          │        │
│   │  math ──────────┼──────────►│ pi              │        │
│   │                 │           │ e               │        │
│   │                 │           │ sin()           │        │
│   │                 │           │ cos()           │        │
│   └─────────────────┘           └─────────────────┘        │
│                                                             │
│   使用方式:math.sqrt()、math.pi                            │
│   必须通过 math 这个"把手"去拿里面的东西                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│              from math import sqrt:只拿"一件东西"          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   from math import sqrt                                     │
│                                                             │
│   你的代码空间                    math 模块(盒子)         │
│   ┌─────────────────┐           ┌─────────────────┐        │
│   │ sqrt() ◄────────┼───────────│ sqrt()          │        │
│   │                 │           │ pi              │        │
│   │                 │           │ e               │        │
│   │                 │           │ sin()           │        │
│   └─────────────────┘           └─────────────────┘        │
│                                                             │
│   使用方式:sqrt()                                          │
│   sqrt 直接放在你的代码空间里,不需要"把手"                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

命名空间的区别

python
# ========== import math ==========
import math

# 当前命名空间只有一个名字:math
print(dir())  # [..., 'math']

# 使用时必须加前缀
print(math.sqrt(16))  # 通过 math 找到 sqrt
print(math.pi)        # 通过 math 找到 pi

# ========== from math import sqrt ==========
from math import sqrt

# 当前命名空间有一个名字:sqrt(不是 math)
print(dir())  # [..., 'sqrt']

# 直接使用,不需要前缀
print(sqrt(16))  # 直接调用

# 注意:pi 不可用!因为没有导入
# print(pi)  # NameError: name 'pi' is not defined

冲突问题演示

python
# ========== 场景:两个模块都有同名函数 ==========

# 方式 1:import(不会冲突)
import math
import cmath  # 复数数学库

# 两个 sqrt 通过不同的前缀区分
print(math.sqrt(4))   # 2.0(实数平方根)
print(cmath.sqrt(-4)) # 2j(复数平方根)
# ✅ 清晰知道用的是哪个模块的 sqrt

# 方式 2:from(会冲突!)
from math import sqrt
from cmath import sqrt  # ⚠️ 覆盖了上一个 sqrt!

print(sqrt(4))   # 用的是 cmath.sqrt
print(sqrt(-4))  # 用的是 cmath.sqrt
# ❌ 第一个 sqrt 被覆盖了,容易出错

内存角度理解

python
import math
# 1. 加载整个 math 模块到内存
# 2. 在当前命名空间创建变量 math,指向模块对象
# 3. 通过 math.xxx 访问模块内容

from math import sqrt
# 1. 加载整个 math 模块到内存(模块都会完整加载)
# 2. 在当前命名空间创建变量 sqrt,指向 math.sqrt
# 3. 直接通过 sqrt 访问,不需要前缀

# 验证:两种方式加载的模块是同一个
import math
from math import sqrt
print(sqrt is math.sqrt)  # True,指向同一个函数对象

命名空间是什么?

命名空间就是一个名字到对象的映射字典。 Python 中每个作用域都有自己的命名空间,导入时只是在当前命名空间里"登记"一个新名字。

┌─────────────────────────────────────────────────┐
│  内置命名空间(built-in)                         │
│  print, len, range, int ...                      │
│                                                  │
│  ┌───────────────────────────────────────────┐  │
│  │  全局命名空间(模块级)                     │  │
│  │  import math    →  {'math': <module>}      │  │
│  │  from math import sqrt → {'sqrt': <func>}  │  │
│  │                                            │  │
│  │  ┌─────────────────────────────────────┐  │  │
│  │  │  局部命名空间(函数内)               │  │  │
│  │  │  def foo():                          │  │  │
│  │  │      x = 1  →  {'x': 1}             │  │  │
│  │  └─────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────┘  │
└─────────────────────────────────────────────────┘

globals() 可以直接查看当前全局命名空间的内容:

python
import math
print(globals())
# {..., 'math': <module 'math' from '...'>}
# 命名空间里登记了名字 'math',对应模块对象

from math import sqrt
print(globals())
# {..., 'sqrt': <built-in function sqrt>}
# 命名空间里登记了名字 'sqrt',直接对应函数对象

两种导入方式的本质区别只在于"登记了什么名字",模块本身都完整加载进了内存:

导入方式命名空间登记的名字访问路径
import mathmath → 模块对象math → 模块 → .sqrt → 函数
from math import sqrtsqrt → 函数对象sqrt → 函数(少一次属性查找)

扩展阅读: 命名空间与作用域的完整规则(LEGB)见 02-核心编程篇/01-函数/03-变量作用域.md

选择建议

┌─────────────────────────────────────────────────────────────┐
│              什么时候用 import,什么时候用 from?            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   推荐用 import 的场景:                                     │
│   ✓ 需要用模块中的多个功能                                  │
│   ✓ 模块名较短(如 os, sys, re)                            │
│   ✓ 多人协作的大型项目                                      │
│   ✓ 不确定是否有命名冲突                                    │
│                                                             │
│   示例:                                                    │
│   import os                                                 │
│   os.path.join('a', 'b')                                    │
│   os.listdir('.')                                           │
│   os.makedirs('dir')                                        │
│                                                             │
│   ─────────────────────────────────────────────────────     │
│                                                             │
│   推荐用 from 的场景:                                       │
│   ✓ 只需要模块中的一两个功能                                │
│   ✓ 函数名很长或使用频繁                                    │
│   ✓ 函数名足够明确,不会混淆                                │
│                                                             │
│   示例:                                                    │
│   from datetime import datetime                             │
│   now = datetime.now()  # 比 datetime.datetime.now() 简洁   │
│                                                             │
│   from collections import defaultdict                       │
│   d = defaultdict(list)  # 比 collections.defaultdict 简洁  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

导入方式的对比总结

方式命名空间使用方式冲突风险推荐场景
import math只有 mathmath.sqrt()大型项目、多用模块功能
from math import sqrt只有 sqrtsqrt()只需少量功能
from math import *所有公开名称直接用❌ 不推荐
import math as m只有 mm.sqrt()模块名较长

导入顺序规范

按照 PEP 8 规范,导入应分组并排序:

python
# 标准库
import os
import sys
from datetime import datetime
from pathlib import Path

# 第三方库
import numpy as np
import pandas as pd
import requests

# 本地模块
from myproject import utils
from myproject.models import User
from myproject.views import home

规范要点:

  1. 三组之间用空行分隔
  2. 每组内按字母顺序排列
  3. importfrom ... import
  4. 避免使用 from module import *

模块属性

__name__ 变量

__name__ 变量用于判断模块是被直接运行还是被导入。

python
# my_module.py

def main() -> None:
    print("程序主逻辑执行")

if __name__ == '__main__':
    main()
else:
    print("模块被导入")

运行结果:

bash
# 直接运行
python my_module.py
# 输出:程序主逻辑执行

# 作为模块导入
python -c "import my_module"
# 输出:模块被导入

常见用法:

python
# my_module.py

def add(a: float, b: float) -> float:
    """加法运算"""
    return a + b

def test() -> None:
    """测试函数"""
    assert add(1, 2) == 3
    assert add(-1, 1) == 0
    print("所有测试通过!")

if __name__ == '__main__':
    test()

其他模块属性

python
# my_module.py
"""
这是模块的文档字符串
用于说明模块的功能
"""

def func() -> None:
    pass

# 查看模块属性
import my_module

print(my_module.__name__)     # 'my_module'(模块名)
print(my_module.__doc__)      # 模块文档字符串
print(my_module.__file__)     # 模块文件路径
print(my_module.__dict__)     # 模块的命名空间字典
print(my_module.__package__)  # 所属包名

添加自定义搜索路径

python
import sys

# 动态添加搜索路径
sys.path.append('/your/custom/path')

# 或添加到开头(优先搜索)
sys.path.insert(0, '/your/custom/path')

# 现在可以导入自定义路径下的模块
import my_module

解决 ModuleNotFoundError:

python
# 遇到 ModuleNotFoundError 时,首先检查:
# 1. 模块是否在当前目录
# 2. 模块是否在 sys.path 中
# 3. 模块名是否正确(区分大小写)
# 4. 是否安装了第三方模块(pip install)

重新加载模块

python
import importlib
import my_module

# 修改了 my_module.py 的代码后,重新加载
importlib.reload(my_module)

# 注意:reload 不会影响已经导入的对象
from my_module import func
importlib.reload(my_module)  # func 仍然是旧版本

控制导出内容(all

__all__ 变量用于控制 from module import * 的行为。

python
# my_module.py

__all__ = ['public_func', 'PUBLIC_VAR']

PUBLIC_VAR: str = "public"
_PRIVATE_VAR: str = "private"

def public_func() -> None:
    """公开函数"""
    pass

def _private_func() -> None:
    """私有函数(下划线开头)"""
    pass

def secret_func() -> None:
    """不在 __all__ 中的函数"""
    pass

使用效果:

python
# main.py
from my_module import *

print(PUBLIC_VAR)    # "public"
public_func()        # 可用

# print(_PRIVATE_VAR)  # NameError(下划线开头)
# _private_func()      # NameError

# secret_func()        # NameError(不在 __all__ 中)

all 的作用:

┌─────────────────────────────────────────────────────────────┐
│                    __all__ 的作用                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   1. 控制 from module import * 的导出内容                   │
│                                                             │
│   2. 明确模块的公开 API                                     │
│                                                             │
│   3. 文档工具(如 pydoc)会参考 __all__                     │
│                                                             │
│   注意:                                                    │
│   • __all__ 只影响 import *                                 │
│   • from module import name 仍然可以导入任意名称            │
│   • 下划线开头的名称默认不会被 import * 导入                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

L2 实践层:用好

推荐做法

做法原因示例
首选 import module避免命名冲突,代码意图清晰import math 而非 from math import *
按 PEP 8 排序导入便于维护,快速定位问题标准库 → 第三方 → 本地,每组内字母排序
if __name__ == "__main__" 保护测试代码模块被导入时不执行测试逻辑if __name__ == "__main__": test()
__all__ 声明公开 API明确模块的公开接口__all__ = ["func_a", "MyClass"]
避免模块顶层副作用代码导入时避免意外行为(网络请求、文件写入等)将初始化逻辑放入函数中
需要多个功能时用 import module命名空间隔离,冲突风险最低import os,使用 os.path.join()

反模式:不要这样做

python
# ❌ 使用 from module import * — 污染命名空间
from math import *
print(sqrt(16))  # sqrt 来自哪里?不清楚

# ✅ 显式导入或使用模块前缀
import math
print(math.sqrt(16))

# ---

# ❌ 导入顺序混乱 — 难维护
from myapp.models import User
import os
import requests
from datetime import datetime

# ✅ 按规范排序:标准库 → 第三方 → 本地
import os
from datetime import datetime

import requests

from myapp.models import User

# ---

# ❌ 顶层副作用 — 导入即执行
# bad_module.py
print("正在连接数据库...")  # 导入时立即执行!
conn = connect_to_database()  # 可能失败,阻塞导入

# ✅ 延迟到调用时执行
# good_module.py
_cache: dict | None = None

def get_connection():
    """按需连接数据库"""
    global _cache
    if _cache is None:
        _cache = connect_to_database()
    return _cache

# ---

# ❌ 在模块顶层执行耗时操作
# slow_module.py
result = expensive_computation()  # 每次导入都要等待

# ✅ 惰性求值
# fast_module.py
def get_result():
    return expensive_computation()

适用场景

场景推荐做法原因
大型项目多人协作import module命名空间隔离,避免冲突
只需模块中一两个函数from module import func减少输入,代码更简洁
模块名较长import long_name as ln保持简洁同时避免冲突
编写库/框架的公开 API设置 __all__明确接口,文档友好
模块同时用于脚本和库if __name__ == "__main__"兼顾直接运行和导入使用
需要重新加载模块(开发时)importlib.reload()避免重启 Python 进程

L3 专家层:深入

Python 如何实现

CPython 中 import 语句的执行流程:

┌──────────────────────────────────────────────────────────────┐
│                    import 语句执行流程                          │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│   import math                                                │
│     │                                                        │
│     ▼                                                        │
│   __import__("math", globals(), locals(), [], 0)             │
│     │                                                        │
│     ▼                                                        │
│   importlib.__import__()                                     │
│     │                                                        │
│     ├── 1. 检查 sys.modules["math"] 是否已存在               │
│     │      ├─ 存在 → 直接返回缓存对象                        │
│     │      └─ 不存在 → 继续查找                             │
│     │                                                        │
│     ├── 2. 遍历 sys.meta_path 查找 Finder                    │
│     │      ├─ BuiltinImporter  → 内置模块(如 sys)         │
│     │      ├─ FrozenImporter   → 冻结模块                   │
│     │      └─ PathFinder        → 文件系统模块              │
│     │           │                                            │
│     │           └── 遍历 sys.path_hooks                      │
│     │                └── 遍历 sys.path 的每个目录            │
│     │                                                        │
│     ├── 3. Finder 返回 Loader + Spec                         │
│     │                                                        │
│     ├── 4. Loader 执行:                                     │
│     │      ├─ 获取源码                                       │
│     │      ├─ 编译为字节码(存入 __pycache__/*.pyc)        │
│     │      └─ exec(code, module.__dict__)                    │
│     │                                                        │
│     └── 5. 将模块对象存入 sys.modules                        │
│                                                              │
└──────────────────────────────────────────────────────────────┘

验证代码:

python
import sys
import importlib

# 查看所有已缓存的模块
print(f"已加载模块数: {len(sys.modules)}")
print(f"math 已加载: {'math' in sys.modules}")

# 查看 sys.meta_path(Finder 链)
for i, finder in enumerate(sys.meta_path):
    print(f"meta_path[{i}]: {type(finder).__name__}")

# 查看 sys.path_hooks
for i, hook in enumerate(sys.path_hooks):
    print(f"path_hooks[{i}]: {hook}")

# 模拟 import 过程
spec = importlib.util.find_spec("math")
print(f"math 的 Spec: {spec}")
print(f"  name: {spec.name}")
print(f"  loader: {spec.loader}")
print(f"  origin: {spec.origin}")   # .py 文件路径
print(f"  cached: {spec.cached}")   # .pyc 文件路径

pycache 字节码

┌──────────────────────────────────────────────────────────────┐
│                  .pyc 文件结构                                 │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│   当 import math 时:                                        │
│                                                              │
│   1. 查找 __pycache__/math.cpython-311.pyc                   │
│   2. 如果 .pyc 存在且比 math.py 新 → 直接加载 .pyc          │
│   3. 如果 .pyc 不存在或已过期 → 编译 math.py → 写入 .pyc    │
│                                                              │
│   .pyc 文件包含:                                            │
│   ┌─────────────────────────────────┐                       │
│   │ Magic Number (4 bytes)          │ 版本标识               │
│   │ Flags (4 bytes)                 │ 编译标记               │
│   │ Timestamp / Hash (取决于 flag)  │ 源文件识别             │
│   │ Source Size (4 bytes)           │ 源码大小               │
│   │ Code Object (marshal 序列化)    │ 实际字节码             │
│   └─────────────────────────────────┘                       │
│                                                              │
│   字节码缓存策略(PEP 3147 / PEP 552):                     │
│   - Python 3.7 及更早:基于时间戳判断是否过期               │
│   - Python 3.12+:默认使用确定性哈希(PEP 552)              │
│   - 可通过 -B 或 PYTHONDONTWRITEBYTECODE 禁用 .pyc          │
│                                                              │
└──────────────────────────────────────────────────────────────┘

验证字节码:

python
import dis
import importlib

# 查看 import 语句的字节码
import_module_code = compile("import math", "<test>", "exec")
dis.dis(import_module_code)
# 输出:
#   0 LOAD_CONST               0 (0)
#   2 LOAD_CONST               1 (None)
#   4 IMPORT_NAME              0 (math)      ← import 操作码
#   6 STORE_NAME               0 (math)

# 查看 from import 的字节码
from_import_code = compile("from math import sqrt", "<test>", "exec")
dis.dis(from_import_code)
#   0 LOAD_CONST               0 (0)
#   2 LOAD_CONST               1 (('sqrt',))
#   4 IMPORT_NAME              0 (math)      ← 同为 IMPORT_NAME
#   6 IMPORT_FROM              1 (sqrt)      ← 额外的属性查找
#   8 STORE_NAME               1 (sqrt)
#  10 POP_TOP

# 检查 .pyc 文件
import pathlib
spec = importlib.util.find_spec("math")
pyc_path = pathlib.Path(spec.cached)
print(f"__pycache__ 路径: {spec.cached}")
print(f".pyc 存在: {pyc_path.exists()}")
print(f".pyc 大小: {pyc_path.stat().st_size} bytes")

性能考量

操作复杂度说明
首次 import moduleO(n·m),遍历路径 + 编译 + 执行n = sys.path 条目数,m = 文件系统操作
重复 import moduleO(1) 字典查找直接从 sys.modules 返回缓存
from module import nameO(1) + 属性查找import module 多一次模块对象属性查找
from module import *O(k) 遍历 + 绑定k = 导出名称数量
sys.path.append()O(1) 列表追加但影响后续所有 import 的查找范围
importlib.reload()O(重新编译 + 重新执行)不走 sys.modules 缓存,强制重新加载

知识关联

┌──────────────────────────────────────────────────────────────┐
│                    模块基础 知识关联图                         │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│   本章(模块基础)                                           │
│     │                                                        │
│     ├── import 语法 ────────► 04-导入机制(绝对/相对导入)    │
│     │                                                        │
│     ├── sys.path ───────────► 02-自定义模块(搜索路径详解)  │
│     │                                                        │
│     ├── sys.modules ────────► 04-导入机制(importlib 内部)  │
│     │                                                        │
│     ├── __pycache__ 字节码 ─► 性能优化 / importlib 机制     │
│     │                                                        │
│     ├── __name__ ───────────► 02-自定义模块(主模块机制)    │
│     │                                                        │
│     ├── __all__ ────────────► 03-包的结构(包级导出控制)    │
│     │                                                        │
│     └── 模块作为对象 ────────► 02-自定义模块(模块对象内部) │
│                                                              │
└──────────────────────────────────────────────────────────────┘

本章小结

┌─────────────────────────────────────────────────────────────┐
│                      模块基础 知识要点                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   模块概念:                                                 │
│   ✓ 模块是包含 Python 代码的 .py 文件                        │
│   ✓ 作用:代码组织、复用、命名空间隔离                       │
│                                                             │
│   import 语法:                                              │
│   ✓ import module                                           │
│   ✓ from module import name                                 │
│   ✓ import module as alias                                  │
│   ✓ 导入顺序:标准库 → 第三方 → 本地                         │
│                                                             │
│   模块属性:                                                 │
│   ✓ __name__:判断运行方式                                  │
│   ✓ __doc__:文档字符串                                     │
│   ✓ __file__:文件路径                                      │
│                                                             │
│   导入过程:                                                 │
│   ✓ 搜索 → 编译 → 执行 → 缓存                               │
│   ✓ 模块只导入一次,可用 reload 强制重载                     │
│                                                             │
│   搜索路径(sys.path):                                     │
│   ✓ 当前目录 → PYTHONPATH → 标准库 → site-packages          │
│   ✓ 可动态添加自定义路径                                    │
│                                                             │
│   缓存机制:                                                 │
│   ✓ sys.modules 存储已加载模块                              │
│   ✓ importlib.reload() 重新加载模块                         │
│                                                             │
│   导出控制:                                                 │
│   ✓ __all__ 控制 import * 的行为                            │
│   ✓ 下划线开头的名称默认不导出                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘