Python 导入优先级:包还是模块?

23 投票
5 回答
6878 浏览
提问于 2025-04-16 06:31

我不太清楚该如何给这个问题起个合适的名字。

情况 1

假设我有以下的文件夹结构。

foo
|
+- bar/__init__.py
|
+- bar.py

如果我有

from foo import bar

我怎么知道是哪个 bar(bar.py 还是 bar/__init__.py)被导入了呢?有没有什么简单的方法可以自动检测到这一点?

情况 2

foo
|
+- foo.py
|
+- other.py

如果 other.py 里有这一行

import foo

我怎么知道是哪个 foo(foo 还是 foo.foo)被导入了呢?同样,有没有什么简单的方法可以自动检测到这一点?

5 个回答

5

我想补充一下被接受的答案。对于 Python 3.3+ 版本,引入了命名空间包,导入的顺序根据 PEP 420 的规定如下:

在导入处理过程中,导入机制会继续遍历父路径中的每个目录,就像在 Python 3.2 中一样。当寻找一个名为 "foo" 的模块或包时,对于父路径中的每个目录:

  • 如果找到了 <directory>/foo/__init__.py,那么就会导入一个常规包并返回。
  • 如果没有找到,但找到了 <directory>/foo.{py,pyc,so,pyd},那么就会导入一个模块并返回。具体的扩展名列表会根据平台和是否指定了 -O 标志而有所不同,这里列出的只是一个代表性的例子。
  • 如果仍然没有找到,但找到了 <directory>/foo 并且它是一个目录,那么就会记录下来,然后继续扫描父路径中的下一个目录。
  • 否则,扫描将继续进行,查看父路径中的下一个目录。

如果扫描完成后没有返回任何模块或包,但至少记录了一个目录,那么就会创建一个命名空间包。

5

在Python的命令行中:

from foo import bar

print bar.__file__

这段代码应该能告诉你哪个文件已经被导入了。

罗伯

11

简单来说,如果一个包和一个模块同名,并且它们在同一个文件夹里,包会优先被使用。

根据文档的说明:

“当你导入一个叫做 spam 的模块时,解释器会先在当前文件夹里找一个叫 spam.py 的文件,然后再去环境变量 PYTHONPATH 指定的文件夹里找。这个过程和 shell 的 PATH 变量类似,就是一系列文件夹的名字。”

这段话有点误导,因为解释器还会查找一个叫 spam 的包(也就是一个名为 spam 的文件夹,里面有一个 __init__.py 文件)。由于文件夹里的条目是按顺序排列的,所以如果包和模块同名,并且在同一个文件夹里,包会优先被找到,因为 spam 这个文件夹会在 spam.py 之前被找到。

需要注意的是,“当前文件夹”是相对于主脚本的路径(也就是 __name__ == '__main__' 为真的那个脚本)。所以如果你在 /home/billg 目录下调用 /foo/bar.py,那么“当前文件夹”指的就是 /foo

撰写回答