【问题标题】:Import paths - the right way?导入路径 - 正确的方式?
【发布时间】:2011-09-21 20:58:28
【问题描述】:

我知道有很多类似或相同的问题,但我仍然无法理解/找到适合我使用模块的方法。 Python 是我最喜欢的语言,我喜欢其中的所有内容,除了使用导入:递归导入(当您尝试引用尚不存在的名称时)、导入路径等。

所以,我有这样一个项目结构:

my_project/
    package1/
        __init__.py
        module1
        module2
    package2/
        __init__.py
        module1
        module2

Package1 可以作为独立单元使用,但也可以通过package2 导入。 我现在在做什么,例如,在package1.module1 我写from package1 import module2,即使用导入模块的完整路径。我这样做是因为如果我使用import module2 - 当模块将从另一个包 (package2) 导入时,这将不起作用。我也不能使用from . import module2——这在直接运行module1 时不起作用。

好的,所以为了让package1.module1 中的from package1 import module2 在这两种情况下都能正常工作(直接运行package1.module1 和从package2 导入它时)我在package1.module1 的开头添加这些行:

import os, sys
currDir = os.path.dirname(os.path.realpath(__file__))
rootDir = os.path.abspath(os.path.join(currDir, '..'))
if rootDir not in sys.path: # add parent dir to paths
    sys.path.append(rootDir)

对我来说这是可行的,但我觉得这不是 pythonic。我是不是做错了什么?

我是否应该始终从项目根目录运行package1.module1?如果是这样,从 IDE 运行它会很不方便——我需要以某种方式在其中设置路径。

更新:我尝试将文件 root.pth 添加到 package1 目录,内容为 ..。但它不起作用——我猜它是为了别的目的。

结论:

  1. 始终使用绝对导入:import package1.module1

  2. 将引导程序添加到根文件夹以将某些模块作为独立脚本启动。这解决了从 IDE 运行脚本的问题,并且是一种 Python 方法。

2007 年 4 月 22 日,Brett Cannon 写道:

此 PEP 是将 if __name__ == "__main__": ... 成语更改为 if __name__ == sys.main: ...让你至少有机会 在使用相对导入的包中执行模块。

通过 python-ideas 运行这个 PEP。也停止了讨论 提出了许多新的想法。 =) 我已经把它们都列在了 被拒绝的想法部分,虽然如果压倒性地支持一个 挺身而出,PEP 可以转移到其中之一。

我在这个和__main__ 的任何其他提议的小玩意上都是-1 机械。唯一的用例似乎是运行发生的脚本 生活在一个模块的目录中,我一直将其视为一个 反模式。要让我改变主意,你必须说服我 不是。

--Guido van Rossum

【问题讨论】:

  • 此注释与导入无关,但python中变量名的约定是word_secondword而不是wordSecondword
  • @Josh Smeaton,嗯,我经常使用scrapy,有时使用PyQt4,他们都使用camelCase,所以我也决定使用它
  • 为了挑剔,我刚刚检查了scrapy docs,他们也使用snake_case。 doc.scrapy.org/0.10/topics/spiders.html。类名是个例外。 variables_are_snake_though。不过我会保留它的:P
  • 我对python很陌生,你能解释一下“引导程序”是什么意思吗?那是根目录中的 init.py 带有一些导入语句吗?
  • 不,这是一个从包中导入所需子模块以启动应用程序的文件。这是一个切入点。在我的入口点在包内之前,现在它上升了一级。我叫它start_package1.py

标签: python path python-import


【解决方案1】:

您的程序的入口点是什么?通常,程序的入口点将位于项目的根目录。由于它位于根目录,因此根目录中的所有模块都是可导入的,前提是其中有一个__init__.py 文件。

所以,用你的例子:

my_project/
    main.py
    package1/
        __init__.py
        module1
        module2
    package2/
        __init__.py
        module1
        module2

main.py 将是您的程序的入口点。因为作为 main 执行的文件会自动放在 PYTHONPATH 上,所以 package1package2 都可以从顶级导入中获得。

# in main.py
from package1.module1 import *
from package1.module2 import *

# in package1.module1
import module2
from package2.module1 import *

# in package2.module1 import *
import module2
from package1.module1 import *

请注意,在上面,package1 和 package2 是相互依赖的。绝不应该是这样。但这只是能够从任何地方导入的示例。

main.py 也不必太花哨。可以很简单:

# main.py

if __name__ == '__main__':
    from package1.module1 import SomeClass
    SomeClass().start()

我想说的是,如果一个模块需要被其他模块访问,那么该模块应该可以作为顶级导入使用。模块不应尝试将自己作为顶级导入(直接在 PYTHONPATH 上)。

如果模块直接包含在项目中,则项目应负责确保满足所有导入。有两种方法可以做到这一点。第一种是在项目文件夹中创建一个引导程序文件,例如main.py。另一种方法是创建一个文件,将所有相关路径添加到 PYTHONPATH,该文件由可能存在的任何入口点加载。

例如:

# setup.py
import sys

def load():
    paths = ['/path1/','/path2/','/path3/']
    for p in path:
        sys.path.insert(0, p)

# entrypoint.py
from setup import load
load()
# continue with program

要带走的主要内容是,模块应该将自己置于路径上。路径应该由程序的入口点自动确定,或者由知道所有相关模块在哪里的安装脚本明确定义。

【讨论】:

  • 我有两个入口点。有时我使用package1 作为独立程序。有时package1 是从另一个入口点导入的。
  • 我的建议是使用引导程序文件,例如我在回答中提到的 main.py。不要从项目根目录以外的任何地方启动程序。替代方案是项目中其他地方的引导程序,它将所有必要的模块添加到sys.path。如果您有一个可以被其他人使用的模块,那么 ELSE 负责确保该模块可用作顶级导入,这一点很重要。
  • 好的。我认为这就是我一直在寻找的答案——做一个引导者。因此,我将在根目录添加一个 py 文件,该文件将在需要时启动 package1 作为独立文件。
【解决方案2】:

我通常将每个包创建为一个可安装包(即,创建一个 setup.py 文件),然后使用 pip 将它们安装到仅用于该项目的 virtualenv 中。

如果仍在开发中,您甚至可以使用 pip -e 安装。

【讨论】:

    【解决方案3】:

    我在这里晚了 5 年.. 但只需执行 export PYTHONPATH=/path1:/path2:(注意尾随“:”) - 这样您的工作目录(从中运行 python)将在路径中。

    【讨论】:

    • 这可能有效,但会破坏模块——同一个模块可以使用不同的__name__s 导入两次。通常,在这种情况下更改 sys.pathPYTHONPATH 是个坏主意。
    • 希望我能更好地理解您的回答。对我来说,在代码中修改了sys.path 的任何东西都感觉不是pythonic。如果有一个入口点,一切都很简单,但对于开发 - 您需要在任意时间运行任意模块!实现它的唯一方法(至少对我有用的唯一方法)是在 IDE 中修改项目的“PYTHONPATH”,这样您就可以从非主入口点进行绝对导入。
    • this
    • 嗯,这是一个有点不同的问题。问题是:“如何使用包运行脚本”,而您的答案是“不要在包中运行脚本!!”。也许对于最终版本,这是一个糟糕的做法,但对于开发来说,它非常有用!目前有两个选项 - 代码级别的sys.path 和系统级别的PYTHONPATH。请解释您对在包中运行模块的问题的解决方案!
    • 我的问题不仅与开发有关,还与生产有关。解决方案在公认的答案中:在顶级目录中创建一个引导/入口点,它将导入所需的模块并从中调用工作函数
    猜你喜欢
    • 1970-01-01
    • 2021-12-09
    • 2020-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-14
    • 1970-01-01
    相关资源
    最近更新 更多