当其他方法都失败时,import语句何时能找到一个模块?

4
简而言之,这是如何发生的?
cternus@astarael:~⟫ python
Python 2.7.12 (default, Jun 29 2016, 14:05:02)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import backports
>>> import imp
>>> imp.find_module('backports')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named backports

imp 模块声称自己是“实现 import 语句所使用机制的接口”。如果是这样,为什么 import 语句可以找到 backports,但 imp.find_module() 却不能呢?

一些背景信息:backports 声称自己是一个“命名空间包”,而不是一个独立的包;其他模块,例如 backports.shutil_get_terminal_size,位于该命名空间中。这构成了 被最终拒绝的 PEP 的基础。我提出这个问题是因为我遇到了 类似的问题 的变体,并正在尝试找到原因。

更奇怪的是:

>>> backports.__file__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__file__'
>>> dir(backports)
['__doc__', '__name__', '__path__']
>>> backports.__path__
['/Library/Python/2.7/site-packages/backports']
>>> import os; os.path.exists(backports.__path__[0])
False

(不,我的系统中没有名为backports或backports.py的文件或目录。)
编辑以澄清:我知道这可能代表了我的系统的奇怪配置状态。我的问题不是“如何修复”,而是“这怎么可能?”

可能应该是“我的Python环境有什么特别之处?”,因为我得到了backports.__file__ == '.../lib/python2.7/site-packages/backports/__init__.pyc'。你是如何安装backports的? - dhke
出于好奇,在执行 import backports 之前,请尝试检查 "backports" in sys.modules,如果已经加载,请在 del sys.modules["backports"] 之后再次导入。 - Tadhg McDonald-Jensen
1
@TadhgMcDonald-Jensen 'backports': <module 'backports' (built-in)> 呃呃呃 - Christian Ternus
1个回答

3
这个模块可以通过安装 python-configparser APT 软件包来使用。

这怎么可能呢?

python-configparser 使用路径配置文件.pth 文件),因此可以实现。
root@ubuntu18.10:/# dpkg -L python-configparser | head | tail -n 1
/usr/lib/python2.7/dist-packages/configparser-3.5.0b2-nspkg.pth

这个文件会在解释器启动时被Python的site模块自动捕获,因为它位于/usr/lib/python2.7/dist-packages/目录下并具有.pth扩展名。正如文档所述

路径配置文件是以name.pth命名的文件,存在于上述四个目录之一中...... 以import开头(后跟空格或制表符)的行将被执行。

文件/usr/lib/python2.7/dist-packages/configparser-3.5.0b2-nspkg.pth包含以下内容:
import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('backports',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('backports', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('backports', [os.path.dirname(p)])));m = m or sys.modules.setdefault('backports', types.ModuleType('backports'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p)
因此,在Python启动时会自动执行这段代码。稍作美化,看起来像这样:
import sys, types, os

has_mfs = sys.version_info > (3, 5)

p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('backports',))
importlib = has_mfs and __import__('importlib.util')
has_mfs and __import__('importlib.machinery')

m = has_mfs and sys.modules.setdefault('backports', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('backports', [os.path.dirname(p)])))

m = m or sys.modules.setdefault('backports', types.ModuleType('backports'))

mp = (m or []) and m.__dict__.setdefault('__path__',[])

(p not in mp) and mp.append(p)

在Python 2中,它的作用是手动创建一个模块对象,通过调用types.ModuleType构造函数(这就是为什么它看起来像),并将其放入sys.modules中,使用sys.modules.setdefault('backports', types.ModuleType('backports'))。添加到sys.modules之后,import backports将只返回该对象。
__path__给出了一些提示。
root@ubuntu18.10:/# python -c 'import backports; print backports.__path__'
['/usr/lib/python2.7/dist-packages/backports']
root@ubuntu18.10:/# dpkg -S /usr/lib/python2.7/dist-packages/backports
python-configparser: /usr/lib/python2.7/dist-packages/backports

我没有名为 backports 的文件或目录

在 Ubuntu 上,正如上面所示,这个软件包带来了 /usr/lib/python2.7/dist-packages/backports 目录,所以我不确定你为什么没有它。也许是另一个行为类似的软件包,或者 MacOS 的变体不同,或者你在调试问题时删除了该目录?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接