Python 从父目录导入并保持 flake8 快乐

Python import from parent directory and keep flake8 happy

此导入工作正常,但在某些方面感觉很脏。主要是它使用 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, but in that case the discussion focused on whether or not it was good to run tests from the end point. In my case 非常相似,我有一系列目录,这些目录的代码使用父级中的助手。

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

是否有更简洁、更 pythonic 的方法来执行此导入?

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

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

例如使用 os.path.dirname(os.path.dirname(__file__))

而不是 os.path.dirname(__file__)[:-5]

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

from ..codeHelpers import completion_message

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

虽然我在这里,但请注意 from ..codeHelpers 是一个 relative 导入,在同一包中导入某些内容时,您应该始终使用它们。 from codeHelpers 是一个 absolute 导入,在 Python 2 中是不明确的(它应该从包中导入还是从不幸命名的 codeHelpers 模块中导入在你的系统上安装了吗?),并且在 Python 3 中实际上禁止作为从同一模块中导入的方式(即它们始终是绝对的)。难点的解释可以看古文PEP 328

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

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

确保在 child_directory 中也添加一个 __init__.py

无论哪种方式,您都必须四处寻找。如果您的主要目标是避免薄片警告

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

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

首先,由于您没有具体说明收到的 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 解释器将检查的第二个位置(第一个仍然是 '',这是本地目录)。