【问题标题】:Python import precedence: packages or modules?Python 导入优先级:包还是模块?
【发布时间】:2011-05-04 19:00:22
【问题描述】:

我不清楚如何正确命名这个问题。

案例 1

假设我有以下目录结构。

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

如果我有

from foo import bar

我如何知道正在导入哪个栏(bar.pybar/__init__.py)?有什么简单的方法可以自动检测到这种情况的发生吗?

案例 2

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

如果 other.py 有该行

import foo

我如何知道正在导入哪个 foo(foo 或 foo.foo)?同样,是否有任何简单的方法可以自动检测到这种情况的发生?

【问题讨论】:

标签: python import packages


【解决方案1】:

在第一种情况下,您尝试从文件 'foo.py' 导入功能栏

在您尝试导入文件“foo.py”的那一秒

【讨论】:

    【解决方案2】:

    包(带有__init__.py 的目录)优先于模块。这个事实的文档很难找到,但你可以在源代码中看到:python 2.7python 3.6(感谢@qff 的发现)。

    您还需要 foo 目录中的 __init__.py 才能使您的示例正常工作。

    如果other.pyfoo/ 内,那么它将加载foo.py(不是目录foo/),因为它会首先在当前目录中查找(除非您使用过 PYTHONPATH 或 sys.path) .

    【讨论】:

    • 你怎么知道它优先? – 我在the Python documentation 中找不到它
    • @qff 我测试过了。我刚才也测试了python3,它具有相同的行为。链接到该事实的官方文档会很好。如果您找到了,请随时编辑我的答案或发表评论,我会对其进行编辑。
    • 找到了! (kind of) – 目录的条目在尝试将每个条目作为包或模块加载之前进行排序。这可以确保首先加载包。 Link to CPython source code
    【解决方案3】:

    TLDR;如果它们位于同一目录中,则包优先于同名模块。

    来自文档:

    "当一个名为spam的模块被导入时,解释器在当前目录中搜索一个名为spam.py的文件,然后在环境变量PYTHONPATH指定的目录列表中搜索。这个语法相同作为shell变量PATH,即目录名列表。”

    这有点误导,因为解释器还会查找一个名为 spam 的包(一个名为 spam 的目录,其中包含一个 __init__.py 文件)。由于目录条目在搜索之前已排序,因此如果它们位于同一目录中,则包优先于具有相同名称的模块,因为 spam 位于 spam.py 之前。

    请注意,“当前目录”是相对于主脚本路径(__name__ == '__main__' is True 所在的路径)。所以如果你在/home/billg调用/foo/bar.py,“当前目录”指的是/foo

    【讨论】:

    • 情况有变化吗?因为我似乎没有按照您的建议添加当前目录。文档现在声明,“当导入名为 spam 的模块时,解释器首先搜索具有该名称的内置模块。如果没有找到,然后在由变量 sys.path。”而且我没有看到列出了“当前目录”。但既然我迷路了,也许我是误会了?
    • 而且(我可能误解了这一切)之前的 stackoverflow 回答指出,“Python 不会将当前目录添加到 sys.path,而是脚本所在的目录。” stackoverflow.com/questions/2325923/…
    • 这似乎没有回答 案例 1
    【解决方案4】:

    来自 python 外壳:

    from foo import bar
    
    print bar.__file__
    

    应该告诉你哪个文件被导入了

    罗伯

    【讨论】:

    • 在 python3 上没有属性 file
    • 这并不意味着在某处指定了行为。
    【解决方案5】:

    我想补充已接受的答案。对于Python 3.3+,引入了命名空间包,按照PEP 420的导入顺序如下:

    在导入处理期间,导入机制将继续遍历父路径中的每个目录,就像在 Python 3.2 中一样。在为父路径中的每个目录查找名为 "foo" 的模块或包时:

    • 如果找到<directory>/foo/__init__.py,则导入并返回常规包。
    • 如果没有,但找到<directory>/foo.{py,pyc,so,pyd},则导入并返回一个模块。扩展的确切列表因平台以及是否指定 -O 标志而异。此处的列表具有代表性。
    • 如果没有,但找到<directory>/foo 并且是一个目录,则记录它并继续扫描父路径中的下一个目录。
    • 否则扫描会继续父路径中的下一个目录。

    如果扫描完成但没有返回模块或包,并且至少记录了一个目录,则创建命名空间包。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-05
      • 1970-01-01
      • 1970-01-01
      • 2011-12-02
      • 2010-10-17
      相关资源
      最近更新 更多