模块、包与脚本入口
学习目标
- 正确导入标准库和自定义模块。
- 理解包、相对导入、
__name__和__main__。 - 能把可复用逻辑与脚本入口分离。
核心知识点
模块是一个 .py 文件,包是可组织多个模块的目录。导入会执行模块顶层代码并缓存到 sys.modules;因此模块顶层应尽量只放定义和轻量配置,执行动作放在函数或入口中。
常用用法
import 导入模块
import math
value = math.sqrt(16)
print(value)
# 输出:4.0使用模块名限定 API,能减少名称冲突并让来源清晰。
from ... import ...
from pathlib import Path
path = Path("notes")
print(path.name)
# 输出:notes只导入少量明确名字时方便;避免 from module import * 污染命名空间。
导入时起别名
import datetime as dt
date = dt.date(2026, 9, 27)
print(date.isoformat())
# 输出:2026-09-27常见别名可以提升可读性,但项目中应保持约定一致。
脚本入口保护
def main() -> int:
return 0
if __name__ == "__main__":
exit_code = main()
else:
exit_code = -1
print(exit_code)
# 输出:-1真实脚本通常在入口里调用 raise SystemExit(main());示例用普通变量避免导入时退出解释器。
查看模块来源
import json
print(json.__name__, json.__file__.endswith("json/__init__.py"))
# 输出:json True__file__ 可能因安装方式变化,不要把绝对路径写入业务逻辑。
重新导入模块
import importlib
import math
same_module = importlib.import_module("math") is math
print(same_module)
# 输出:True动态导入适合插件系统;普通代码优先使用静态 import,更利于 IDE 和静态检查。
包的推荐结构
package_layout = [
"project/",
" __init__.py",
" cli.py",
" service.py",
]
print("\n".join(package_layout))
# 输出:project/\n __init__.py\n cli.py\n service.py包目录把职责拆开,入口模块负责命令行,服务模块负责可测试逻辑。
不常用但需要知道
相对导入
relative_import = "from .service import build_report"
print(relative_import.startswith("from ."))
# 输出:True相对导入只在包上下文中可靠;直接执行含相对导入的文件可能出现“无已知父包”错误,应使用 python -m package.module。
__all__
__all__ = ["public_name"]
public_name = "visible"
_internal_name = "hidden"
print(__all__, public_name)
# 输出:['public_name'] visible__all__ 主要约定 from module import * 的公开名字,不是访问控制机制。
importlib.reload()
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;不要在其中放重型副作用。- 循环导入通常说明职责耦合,优先拆分公共模块或延迟导入。
课后小问
为什么把主要逻辑放到
main()而不是模块顶层? 答案:便于导入复用、测试,也避免导入时立即产生副作用。 解析:入口保护只在直接运行时调用main,其他模块导入时只加载定义。什么时候使用
python -m package.module? 答案:需要以包上下文运行模块,尤其是模块含相对导入时。 解析:-m让解释器按模块路径定位并设置包信息,比直接运行文件路径更可靠。
本节小结
模块解决复用和命名空间,包解决组织;清晰的入口保护和少副作用的顶层代码,是可维护 Python 项目的基础。
快速回顾
- 导入:
import、from ... import - 别名:
import ... as ... - 入口:
if __name__ == "__main__" - 包运行:
python -m package.module - 动态导入:
importlib.import_module
阿源的知识库