Skip to content

模块、包与脚本入口 ​

学习目标 ​

  • 正确导入标准库和自定义模块。
  • 理解包、相对导入、__name__ 和 __main__。
  • 能把可复用逻辑与脚本入口分离。

核心知识点 ​

模块是一个 .py 文件,包是可组织多个模块的目录。导入会执行模块顶层代码并缓存到 sys.modules;因此模块顶层应尽量只放定义和轻量配置,执行动作放在函数或入口中。

常用用法 ​

import 导入模块 ​

python
import math

value = math.sqrt(16)
print(value)
# 输出:4.0

使用模块名限定 API,能减少名称冲突并让来源清晰。

from ... import ... ​

python
from pathlib import Path

path = Path("notes")
print(path.name)
# 输出:notes

只导入少量明确名字时方便;避免 from module import * 污染命名空间。

导入时起别名 ​

python
import datetime as dt

date = dt.date(2026, 9, 27)
print(date.isoformat())
# 输出:2026-09-27

常见别名可以提升可读性,但项目中应保持约定一致。

脚本入口保护 ​

python
def main() -> int:
    return 0


if __name__ == "__main__":
    exit_code = main()
else:
    exit_code = -1
print(exit_code)
# 输出:-1

真实脚本通常在入口里调用 raise SystemExit(main());示例用普通变量避免导入时退出解释器。

查看模块来源 ​

python
import json

print(json.__name__, json.__file__.endswith("json/__init__.py"))
# 输出:json True

__file__ 可能因安装方式变化,不要把绝对路径写入业务逻辑。

重新导入模块 ​

python
import importlib
import math

same_module = importlib.import_module("math") is math
print(same_module)
# 输出:True

动态导入适合插件系统;普通代码优先使用静态 import,更利于 IDE 和静态检查。

包的推荐结构 ​

python
package_layout = [
    "project/",
    "  __init__.py",
    "  cli.py",
    "  service.py",
]
print("\n".join(package_layout))
# 输出:project/\n  __init__.py\n  cli.py\n  service.py

包目录把职责拆开,入口模块负责命令行,服务模块负责可测试逻辑。

不常用但需要知道 ​

相对导入 ​

python
relative_import = "from .service import build_report"
print(relative_import.startswith("from ."))
# 输出:True

相对导入只在包上下文中可靠;直接执行含相对导入的文件可能出现“无已知父包”错误,应使用 python -m package.module。

__all__ ​

python
__all__ = ["public_name"]
public_name = "visible"
_internal_name = "hidden"
print(__all__, public_name)
# 输出:['public_name'] visible

__all__ 主要约定 from module import * 的公开名字,不是访问控制机制。

importlib.reload() ​

python
import importlib
import math

reloaded = importlib.reload(math)
print(reloaded is math)
# 输出:True

重载会重新执行模块代码,但旧对象的引用可能仍指向旧定义,适合交互探索,不宜作为应用热更新方案。

易混点 ​

  • import package.module 后访问通常写 package.module.name;from 导入后直接使用名字。
  • 模块只在当前解释器进程中缓存,文件改变不代表已导入对象自动更新。
  • python file.py 与 python -m package.module 的导入上下文不同。
  • __init__.py 可为空,也可暴露包级 API;不要在其中放重型副作用。
  • 循环导入通常说明职责耦合,优先拆分公共模块或延迟导入。

课后小问 ​

  1. 为什么把主要逻辑放到 main() 而不是模块顶层? 答案:便于导入复用、测试,也避免导入时立即产生副作用。 解析:入口保护只在直接运行时调用 main,其他模块导入时只加载定义。

  2. 什么时候使用 python -m package.module? 答案:需要以包上下文运行模块,尤其是模块含相对导入时。 解析:-m 让解释器按模块路径定位并设置包信息,比直接运行文件路径更可靠。

本节小结 ​

模块解决复用和命名空间,包解决组织;清晰的入口保护和少副作用的顶层代码,是可维护 Python 项目的基础。

快速回顾 ​

  • 导入:import、from ... import
  • 别名:import ... as ...
  • 入口:if __name__ == "__main__"
  • 包运行:python -m package.module
  • 动态导入:importlib.import_module