【问题标题】:Python import from parent directory and keep flake8 happyPython 从父目录导入并保持 flake8 快乐
【发布时间】:2017-03-16 04:08:14
【问题描述】:

此导入工作正常,但在某些方面感觉很脏。主要是它使用 slice* 中的特定数字来获取父路径,并且它会惹恼 flake8 linter。

import os
import sys
sys.path.append(os.path.dirname(__file__)[:-5])
from codeHelpers import completion_message

它在一个看起来有点像这样的文件系统中:

parent_folder
    __init__.py
    codeHelpers.py

    child_folder
        this_file.py

(child_folder 实际上称为week1,因此切片中的 5)

这个问题与Python import from parent directory 非常相似,但在这种情况下,讨论的重点是从终点运行测试是否好。 In my case,我有一系列目录,其中包含使用位于父级中的帮助器的代码。

上下文:每个目录都是一组每周练习,所以我想让它们尽可能简单。

是否有一种更简洁、更 Python 的方式来执行此导入?

@cco 解决了数字问题,但它仍然让 linter 感到不安。

【问题讨论】:

  • 您有很多答案可以解决您的问题,如果您可以将其中一个标记为已回答,我相信他们会很感激的。

标签: python python-import flake8


【解决方案1】:

首先,由于您没有具体说明您遇到了哪个 lint 错误,我假设这是因为您在 sys.path.append 之后有一个导入。

最简洁的方法是使用相对或绝对导入。

使用绝对导入:

from parent_path.codeHelpers import completion_message

使用相对导入:

from ..codeHelpers import completion_message

对于原始问题中列出的简单示例,这应该是所有必需的。它很简单,它是 pythonic,它是可靠的,并且它修复了 lint 问题。

您可能会发现自己处于上述情况不适用于您并且仍然需要sys.path 操作的情况。缺点是您的 IDE 可能无法解决从新路径导入模块的问题,从而导致自动代码完成无法正常工作以及将导入标记为错误等问题,即使代码可以正常运行。

如果你发现你仍然需要使用 sys.path 并且想要避免这种情况下的 lint 错误,请创建一个新模块并在其中执行 sys.path 操作。然后确保在需要修改 sys.path 的任何模块之前导入新模块。

例如:

local_imports.py

"""Add a path to sys.path for imports."""

import os
import sys

# Get the current directory
current_path = os.path.dirname(__file__)

# Get the parent directory
parent_path = os.path.dirname(current_path)

# Add the parent directory to sys.path
sys.path.append(parent_path)

然后在目标文件中:

import local_imports  # now using modified sys.path
from codeHelpers import completion_message

这样做的缺点是它要求您在每个child_folder 中包含local_imports.py,如果文件夹结构发生变化,您将不得不修改每个local_imports 文件。

这种模式真正有用的地方是当您需要在包中包含外部库(例如在libs 文件夹中)而不需要用户自己安装库时。

如果您将此模式用于libs 文件夹,您可能需要确保您包含的库优先于已安装的库。

为此,改变

sys.path.append(custom_path)

到

sys.path.insert(1, custom_path)

这将使您的自定义路径成为 Python 解释器将检查的第二个位置(第一个仍然是 '',这是本地目录)。

【讨论】:

    【解决方案2】:

    您可以使用.. 从模块导入包中的更高级别。在 this_file.py 中:

    from ..codeHelpers import completion_message
    

    如果您想更上一层楼,只需不断添加点...

    当我在这里时,请注意from ..codeHelpers 是一个相对 导入,在同一个包中导入某些内容时,您应该始终使用它们。 from codeHelpers 是 absolute 导入,在 Python 2 中是不明确的(应该从包中导入还是从您系统上安装的不幸命名为 codeHelpers 的模块中导入?),在 Python 中3 实际上禁止作为从同一模块中导入的一种方式(即它们始终是绝对的)。您可以阅读古代的PEP 328 来了解其中的困难。

    【讨论】:

    • 名字有点可惜,code是学位的名字,computational design。这就是逻辑,但我可以看到我已经调用了我的帮助文件programmingStuff
    • 我从..codeHelpers 尝试过,但我得到了ValueError: Attempted relative import in non-package error。这是否意味着其他地方存在问题,还是意味着我需要从不同的文件调用 this 文件而不是直接运行它?
    • 所以我认为这意味着您正在直接运行 this_file.py。通常我会建议不要混合模块和脚本代码。但是如果你想把一个包也做成一个脚本,那么这样做的方法就是使用__main__.py 机制。基本上,您可以将 this_file.py 重命名为 child_folder 中的 __main__.py 文件,并通过运行 python -m parent_folder.child_folder 来执行它,确保在 parent_folder 和 child_folder 中都有 __init__.py 文件(如果使用 python 2;在 python 3 中是不必要的)。
    【解决方案3】:

    使用绝对导入路径可能更容易,如下所示:

    from parent_folder.code_helpers import completion_message
    

    但这需要您确保设置 PYTHONPATH 环境变量,以便它可以看到最高的根目录(我认为在这种情况下为parent_folder)。例如,

    PYTHONPATH=. python parent_directory/child_directory/this_file.py
    # here the '.' current directory would contain parent_directory
    

    确保也将__init__.py 添加到 child_directory。

    【讨论】:

      【解决方案4】:

      您可以通过应用os.path.dirname 两次来消除关于最终目录名称长度的假设。

      例如而不是os.path.dirname(__file__)[:-5],使用os.path.dirname(os.path.dirname(__file__))

      【讨论】:

        【解决方案5】:

        无论哪种方式,您都必须破解。如果你的主要目标是避免片状警告

        • 添加noqa 评论
        • exec(open("../codeHelpers.py").read(), globals())

        • 您可以使用解释器选项 -c 传递文件名(不应该打扰 flakes8)

        【讨论】:

          猜你喜欢
          • 2013-11-09
          • 2016-12-27
          • 2012-07-08
          • 2020-03-21
          • 2021-08-15
          • 1970-01-01
          • 2015-07-23
          • 2021-10-23
          • 1970-01-01
          相关资源
          最近更新 更多