Python 导入优先级:包还是模块?
我不太清楚该如何给这个问题起个合适的名字。
情况 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 个回答
我想补充一下被接受的答案。对于 Python 3.3+
版本,引入了命名空间包,导入的顺序根据 PEP 420 的规定如下:
在导入处理过程中,导入机制会继续遍历父路径中的每个目录,就像在 Python 3.2 中一样。当寻找一个名为
"foo"
的模块或包时,对于父路径中的每个目录:
- 如果找到了
<directory>/foo/__init__.py
,那么就会导入一个常规包并返回。- 如果没有找到,但找到了
<directory>/foo.{py,pyc,so,pyd}
,那么就会导入一个模块并返回。具体的扩展名列表会根据平台和是否指定了-O
标志而有所不同,这里列出的只是一个代表性的例子。- 如果仍然没有找到,但找到了
<directory>/foo
并且它是一个目录,那么就会记录下来,然后继续扫描父路径中的下一个目录。- 否则,扫描将继续进行,查看父路径中的下一个目录。
如果扫描完成后没有返回任何模块或包,但至少记录了一个目录,那么就会创建一个命名空间包。
在Python的命令行中:
from foo import bar
print bar.__file__
这段代码应该能告诉你哪个文件已经被导入了。
罗伯
简单来说,如果一个包和一个模块同名,并且它们在同一个文件夹里,包会优先被使用。
根据文档的说明:
“当你导入一个叫做
spam
的模块时,解释器会先在当前文件夹里找一个叫spam.py
的文件,然后再去环境变量PYTHONPATH
指定的文件夹里找。这个过程和 shell 的 PATH 变量类似,就是一系列文件夹的名字。”
这段话有点误导,因为解释器还会查找一个叫 spam
的包(也就是一个名为 spam
的文件夹,里面有一个 __init__.py
文件)。由于文件夹里的条目是按顺序排列的,所以如果包和模块同名,并且在同一个文件夹里,包会优先被找到,因为 spam
这个文件夹会在 spam.py
之前被找到。
需要注意的是,“当前文件夹”是相对于主脚本的路径(也就是 __name__ == '__main__'
为真的那个脚本)。所以如果你在 /home/billg
目录下调用 /foo/bar.py
,那么“当前文件夹”指的就是 /foo
。