Python 可以导入未安装的模块

Python can import a module that isn't installed

所以,我正在尝试打包我编写的 python 脚本,它有一个子模块,我们称它为 submodule。文件夹结构如下所示:

cool_script/
  setup.py
  cool_script.py
  submodule/
    __init__.py
    implementation.py

现在,经过多次 pip install .pip install -e . 调用后,我的情况是 submodule 可以全局导入。无论在我系统的哪个位置,这总是有效的:

$ python3
[...]
>>> import submodule
>>> submodule.__file__
'/home/me/fake/path/cool_script/submodule/__init__.py'

但是我不知道为什么。

我做的包又被卸载了,pip在其索引中找不到子模块。 dist-packages 中也没有任何内容,我手动删除了仍然坐在那里的 cool_script.egg-link:

$ ls /usr/local/lib/python3.4/dist-packages | ack cool 
$ ls /usr/local/lib/python3.4/dist-packages | ack submodule
$ 

PYTHONPATH也是空的:

$ echo $PYTHONPATH

$

为什么Python知道submodule的位置?我怎样才能知道?

第一个运行python -c "import site; print(site.getsitepackages())"。它将打印如下列表:

['/XXX/something/site-packages']

通常此列表中只有一个路径,它指向 pip 安装脚本的目录。如果你好奇,你可以 ls 进去:ls /XXX/something/site-packages/.

不过,更有趣的是,当您使用开发人员安装 (a.k.a.pip install -e) 时,pip 会在该目录中放置一个 "link" 文件。 "link" 文件以原始项目命名,末尾带有 .egg-link 扩展名。

所以您可能在该目录中有一个 cool_script.egg-link 文件。如果您尝试将其打印出来,您应该会发现其内容列出了模块的原始文件系统位置。类似于:

$ cat /XXX/something/site-packages/cool_script.egg-link
/home/me/fake/path/cool_script/
.

这就是 pip 记录它在开发者模式下安装了某些东西的方式,但并不是 Python 实际上知道如何找到您的模块的方式(那太容易了,对吧:-)).

Python 不知道 .egg-link 个文件,但它读取 site-packages 目录中的所有 .pth 个文件以获得 sys.path 的附加路径(*).因此,为了 Python 能够导入开发者模式安装,pip 将他们所有的路径写入一个通常称为 easy-install.pth.pth 文件中(因为旧的 easy-install 工具实际上开创了该技术)。如果您打印出该文件,您将获得在开发者模式下安装的 所有 项目路径的列表:

$ cat /XXX/something/site-packages/easy-install.pth
/home/me/fake/path/cool_script/
/home/me/another/project/

并且您可以检查 easy-install.pth 中列出的所有路径是否确实已添加到您的 sys.path

(*) 从技术上讲,Python 中读取那些 .pth 文件的部分是通常在启动时自动导入的 site 模块。不过,有一个选项可以禁用 site 模块,例如使用 python -S。在这种情况下,您会看到 sys.path 既不包含 site-packages 目录也不包含开发人员安装路径。