【问题标题】:How do implicit relative imports work in Python?隐式相对导入如何在 Python 中工作?
【发布时间】:2018-06-26 12:33:45
【问题描述】:

假设我有以下文件,

pkg/
pkg/__init__.py
pkg/main.py # import string
pkg/string.py # print("Package's string module imported")

现在,如果我运行 main.py,它会显示 "Package's string module imported"

这是有道理的,它按照 link 中的声明工作:

"它会首先在包的目录中查找"

假设我稍微修改了文件结构(添加了一个核心目录):

pkg/
pkg/__init__.py
plg/core/__init__.py
pkg/core/main.py # import string
pkg/string.py # print("Package's string module imported")

现在,如果我运行 python core/main.py,它会加载内置的 string 模块。

在第二种情况下,如果它必须遵守声明“它将首先查看包的目录”不应该加载本地string.py,因为pkg是“包目录”?

我对“包目录”一词的理解是具体而言根文件夹,其中包含__init__.py文件夹集合。所以在这种情况下,pkg 是“包目录”。它适用于main.py,也适用于core/main.py 等子目录中的文件,因为它是这个“包”的一部分。

这在技术上正确吗?

PS:sn-p 代码中# 后面的内容是文件的实际内容(没有前导空格)。

【问题讨论】:

    标签: python module python-2.x


    【解决方案1】:

    包是带有__init__.py 文件的目录,是的,并且在在模块搜索路径中找到时作为模块加载。所以pkg 只是一个包,如果父目录在模块搜索路径上,您可以导入并视为包。

    但是通过将pkg/core/main.py 文件作为脚本 运行,Python 将pkg/core 目录添加到模块搜索路径中,而不是pkg 的父目录。现在,您的模块搜索路径上确实有一个 __init__.py 文件,但这不是定义包的内容。你只有一个__main__ 模块,与其他任何东西没有包关系,你不能依赖隐式的相对导入。

    您有三个选择:

    1. 不要将包内的文件作为脚本运行。将脚本文件放在您的包外部,并根据需要导入您的包。您可以将其next 放到pkg 目录中,或者确保首先将pkg 目录安装到已经在模块搜索路径上的目录中,或者让您的脚本计算正确的路径添加到sys.path

    2. 使用-m command line switch 像运行脚本一样运行模块。如果您使用python -m pkg.core,Python 将查找__main__.py 文件并将其作为脚本运行。 -m 开关会将当前工作目录添加到您的模块搜索路径中,因此您可以在正确的工作目录中使用该命令并且一切正常。或者将您的包安装在模块搜索路径上的目录中。

    3. 让您的脚本将正确的目录添加到模块搜索路径(基于os.path.absolute(__file__) 以获取当前文件的路径)。考虑到您的脚本总是命名为__main__,并且导入pkg.core.main 会添加第二个独立的模块对象;你会有两个独立的命名空间。

    我还强烈建议不要使用隐式相对导入。您可以通过添加同名的嵌套包或模块轻松地屏蔽顶级模块和包。如果您尝试在pkg 包中使用import time,则pkg/time.py 将位于标准库time 模块之前。相反,使用 Python 3 模型的 explicit 相对模块引用;将from __future__ import absolute_import 添加到您的所有文件中,然后使用from . import <name> 明确说明您的模块是从哪里导入的。

    【讨论】:

    • 谢谢。我认为这涵盖了我的疑问以及我遇到的从模块运行脚本的原始问题。但是我应该澄清的一件小事是,通过“包目录”,我想到的是带有__init__.py 的文件夹集合的根目录。原因是当我说zope 包时,我通常指的是根文件夹。看起来“包”这个词被重载来表示不同的东西。可能您想根据该假设编辑第一行:-)
    • @Nishant:zope 可能是包的根文件夹,但该文件夹本身并未添加到 sys.path 模块搜索路径中。您希望该文件夹的 父目录 位于模块搜索路径中。
    • 谢谢@Martijn Pieters,这是非常重要的一点。我不知道。
    • @Nishant:但是,一个包可以有更多的嵌套包。所以zope.foo 也可以是一个包,如果zope/foo 是一个带有zope/foo/__init__.py 文件的目录。所以 package directory 是任何包含__init__.py 文件的目录。
    猜你喜欢
    • 2014-01-09
    • 2018-07-20
    • 2010-12-07
    • 1970-01-01
    • 2020-11-27
    • 2023-03-20
    • 2021-09-12
    • 2023-03-03
    • 1970-01-01
    相关资源
    最近更新 更多