在典型的 Python 安装中,ExtensionFileLoader 类优先于用于.py 文件的SourceFileLoader。它是处理.pyd 文件导入的ExtensionFileLoader,在Windows 机器上,您会发现.pyd 注册在importlib.machinery.EXTENSION_SUFFIXES 中(注意:在Linux/macOS 上它将有.so 代替)。
因此,在同一目录内发生名称冲突的情况下(这意味着按顺序查看 sys.path 时出现“平局”),a.pyd 文件优先于 a.py 文件。您可以验证在创建空的a.pyd 和a.py 文件时,import a 语句会尝试加载 DLL(当然会失败)。
要查看 CPython 源代码中的优先级,请查看 here in importlib._bootstrap_external. _get_supported_file_loaders:
def _get_supported_file_loaders():
"""Returns a list of file-based module loaders.
Each item is a tuple (loader, suffixes).
"""
extensions = ExtensionFileLoader, _imp.extension_suffixes()
source = SourceFileLoader, SOURCE_SUFFIXES
bytecode = SourcelessFileLoader, BYTECODE_SUFFIXES
return [extensions, source, bytecode] # <-- extensions before source!
有关文档参考,请参阅http://www.python.org/doc/essays/packages/
如果我有同名的模块和包怎么办?
您可能有一个目录(在 sys.path 上),其中包含一个模块 spam.py 和一个包含 _init_.py 的子目录 spam(没有 _init_.py,目录未被识别为包)。在这种情况下,子目录具有优先权,导入 spam 将忽略 spam.py 文件,而是加载包 spam。如果您希望模块 spam.py 具有优先权,则必须将其放在 sys.path 中较早的目录中。
(提示:搜索顺序由函数imp.get_suffixes()返回的后缀列表决定。通常按照以下顺序搜索后缀:".so", "module.so", “.py”、“.pyc”。目录没有显式出现在此列表中,但位于其中的所有条目之前。)
该文档没有明确提及“.pyd”,但这是“.so”的 Windows 等效项。我刚刚在Windows机器上测试过,确实'.pyd'出现在后缀列表中'.py'之前。
请注意,上面给出的参考资料已经很老了!自从写这篇文章以来,导入系统已经彻底改造,底层机制已经暴露给用户访问:例如,你可以改变sys.meta_path 来注册你自己的加载器或更改优先级。因此,现在可以自定义 '.py' 优先于 '.pyd',imp.get_suffixes() 对任何事情的看法并不重要(实际上,该功能现在已被弃用)。当然,默认的 Python 安装不会这样做,并且默认优先级与上面提到的参考保持相同。