跳转至

9.2 模块与包——模块进阶

内容梗概

本课讲解模块的两个进阶话题。第一是模块的搜索路径:导入模块时 Python 先查内置模块,再按 sys.path 列表的顺序逐个目录查找 .py 文件;sys.path 由当前脚本目录、PYTHONPATH 环境变量和 Python 安装目录三部分组成。由此引出"文件名不要与标准库同名"的重要教训,并用自建的假 turtle.py 覆盖真库的演示加以说明,同时区分了真正内置的模块(如 sys、os)不可被覆盖这一例外。第二是模块的编译缓存机制:导入模块时 Python 会把 .py 编译成字节码 .pyc 存入 __pycache__ 目录,源码未变时下次直接加载以提升效率,老师还顺带提到 .pyc 逆向在 CTF 比赛中的应用。

知识点详解

模块的搜索路径(约 00:00–05:45)

  • 问题:运行 python cuc.py 时,其中 import calc,Python 按什么逻辑找到对应的 .py 文件?
  • 搜索规则:
  • 先找**内置模块**——如 sys 这样内嵌在 Python 解释器里的特殊模块,不走 sys.path 搜索,数量很少。
  • 其余模块去 sys.path(一个列表)对应的各个路径中,**按顺序、按优先级**挨个查找,找到即导入,找不到就继续下一个目录。
  • 类比:与操作系统的环境变量(Windows 查找 exe 的 PATH)是同样的思路。
  • sys.path 的三部分构成(约 03:08):
  • 当前目录:即被运行脚本(如 cuc.py)所在的目录,排在最前。
  • PYTHONPATH 环境变量:若操作系统中定义了该变量,其中的路径会被加入列表(老师机器上未定义)。
  • Python 安装目录下的相关路径,包括:zip 包(可直接搜索 zip 内模块)、DLLs 目录、lib 目录(放默认自带的库,如 os)、安装根目录、用户目录下的 Python 目录,以及 lib/site-packages——用 pip install 安装的第三方库都放在这里。

同名文件覆盖标准库的问题(约 05:45–13:48)

  • 由于 sys.path 中当前目录优先级最高,若在自己的脚本目录下新建了与库同名的 .py 文件,会**覆盖**后面路径中的真正库。
  • 演示:课程最开始用过 turtle 库画小乌龟的例子。在当前目录建一个 turtle.py,里面只写一句 print("wrong turtle");再运行画图程序时,导入的是这个假 turtle,调用 turtle.Screen() 初始化画布时报错(假模块里没有 Screen),真正的 turtle 库被覆盖。
  • 重要结论(老师反复强调):创建文件时不要起与 Python 自带库同名的文件名,避免冲突;不确定有哪些自带库就去查文档。文件名写得完整、稍长一点更稳妥。
  • 例外情况(约 09:49):即使建了 sys.py,import sys 仍然导入真正的 sys 模块。原因是 Python 自带的模块分几层:
  • 真正的内置模块(sys、os 等,内嵌解释器)——搜索优先级最高,无法被同名文件覆盖;
  • 安装时自带但非内置的模块(如 turtle)——会被覆盖;
  • 用 pip 安装的第三方模块——也会被覆盖。
  • 偷懒记法:反正尽量不起同名名字就对了。
  • 排错思路(约 13:25):如果某个明明已安装/自带的库不能正常工作,要想到搜索优先级问题——**检查当前目录下有没有同名文件**导致导入了错误的库。

模块的编译与 __pycache__(约 13:49–17:44)

  • 回顾:Python 是解释型语言,但解释器执行前会先把源码 compile(编译)成字节码,再由 PVM(Python Virtual Machine,Python 虚拟机)运行字节码。
  • 字节码即编译后得到的 .pyc 文件,执行和加载速度更快、效率更高。
  • 在 cuc.py 中导入模块后,目录下会自动生成 __pycache__(双下划线包裹,Python 保留命名)目录,里面是刚导入的模块编译出的 .pyc 文件。
  • 命名规则:模块名.cpython-37.pyc——模块名 + 解释器标识(CPython,即 C 语言实现的解释器)+ 版本号(如 3.7、3.8)。
  • 机制:第一次加载时编译并缓存;第二次加载时若发现源码未更新,直接加载字节码,提高运行效率(Python 运行效率常被诟病,此机制是一种弥补)。
  • 拓展(约 17:11):对打 CTF 比赛的同学,.pyc 是一个突破口——对方可能只给编译好的字节码而不给源码,可以分析字节码**逆向**推出模块功能。

示例与演示

  • 打印 sys.path:展示列表中的多个路径,逐一对照讲解其来源(当前目录 → 环境变量 → 安装目录各子目录 → site-packages),并现场打开 Python 安装目录查看 DLLs、lib、site-packages 等文件夹。
  • 假 turtle.py 覆盖演示:复制课程开头的小乌龟画图程序,在当前目录建同名 turtle.py,运行后先打印假模块的语句,随后 Screen 调用报错;再演示建 sys.py 却不影响 import sys,说明内置模块不可覆盖。
  • __pycache__ 演示:导入模块后查看自动生成的缓存目录及 calc.cpython-37.pyc 等文件,解释其命名与作用。

重点与难点

  • sys.path 的搜索顺序:当前脚本目录优先级最高,这是同名文件覆盖标准库的根源。
  • 不要给文件起与标准库/第三方库同名的名字;遇到"库明明装了却不能用"的 bug,先查当前目录有无同名文件。
  • 区分三类模块的可覆盖性:真正内置的(sys、os)不可覆盖;自带非内置的(turtle)和 pip 第三方库会被当前目录同名文件覆盖。
  • .pyc 字节码与 __pycache__ 是自动产生的缓存,日常很少直接操作,但要知道该机制的存在。

关联内容

  • 上接 9.1 模块基础:模块的导入方式、sys 模块(本课的 sys.path 即其属性之一)。
  • 呼应课程早期内容:turtle 小乌龟画图示例(第 1 章课程概述中的实例);Python 解释执行与编译为字节码的原理(Python 语言介绍部分);相对路径与环境变量的概念。
  • 下接 9.3 包:模块组织为文件夹形成包;第 10 章将讲 pip install 安装第三方库(对应本课的 site-packages)与虚拟环境管理。
  • 拓展方向:CTF 中的 .pyc 逆向分析。