在Python中如何进行相对导入?

615

想象一下这个目录结构:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

我正在编写mod1,需要从mod2中导入一些内容。我该怎么做?

我尝试了from ..sub2 import mod2,但是出现了“在非包中尝试相对导入”的错误。

我在谷歌上搜索了一下,但只找到了"sys.path操纵"的技巧。难道没有更简单的方法吗?


我所有的__init__.py目前都是空的。

我这样做是因为sub2包含了在子包(sub1subX等)之间共享的类。

我想要的行为与PEP 366感谢John B)中描述的相同。


9
我建议您更新问题,更清晰地描述您所描述的PEP 366中提到的问题。 - John B
2
这是一个冗长的解释,但请查看此处:https://dev59.com/RG855IYBdhLWcg3wbztk#10713254 我回答了一个非常类似的问题。我也遇到了同样的问题,直到昨晚。 - Sevvy325
3
对于那些希望加载位于任意路径的模块的人,请参考此链接:https://dev59.com/anVD5IYBdhLWcg3wKYP- - Evgeni Sergeev
2
相关的是,Python 3 将默认将导入处理方式更改为默认情况下为绝对路径;相对导入必须明确指定。 - Ross
18个回答

8

7
正如 John B. 所说的那样,从 'main' 模块中无法进行相对导入。 - PuercoPop

2

来自Python文档,

在Python 2.5中,您可以使用from __future__ import absolute_import指令将导入的行为切换到绝对导入。在未来版本(可能是Python 2.7)中,这种绝对导入行为将成为默认值。一旦绝对导入成为默认值,import string将始终找到标准库的版本。建议用户尽可能开始使用绝对导入,因此最好在代码中开始编写from pkg import string


2

除了John B所说的,似乎设置__package__变量会更有帮助,而不是改变__main__,这可能会影响其他事情。但就我所测试的来看,它并不能完全按照应该的方式工作。

我也遇到了同样的问题,PEP 328PEP 366都无法完全解决问题,因为在一天结束时,两者都需要将包的头部包含在sys.path中,就我所理解的而言。


1
我发现将“PYTHONPATH”环境变量设置为顶层文件夹更容易:
bash$ export PYTHONPATH=/PATH/TO/APP

然后:

import sub1.func1
# ...more imports

当然,PYTHONPATH是“全局”的,但它现在还没有给我带来麻烦。


这基本上是virtualenv让您管理导入语句的方式。 - byxor

0
一种不太正规的方法是在运行时将当前目录追加到 PATH 中,方法如下:
import pathlib   
import sys
sys.path.append(pathlib.Path(__file__).parent.resolve())
import file_to_import  # the actual intended import

与另一种解决方案不同的是,这里使用了pathlib而不是os.path


0
你需要将模块的路径添加到 PYTHONPATH 中:
export PYTHONPATH="${PYTHONPATH}:/path/to/your/module/"

3
这与操作 sys.path 大致相同,因为 sys.path 是从 PYTHONPATH 初始化的。 - Joril
@Joril 是正确的,但是 sys.path 需要在源代码中硬编码,而 PYTHONPATH 是一个环境变量,可以被导出。 - Giorgos Myrianthous

0

该方法查询并自动填充路径:

import os
import inspect
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
os.sys.path.insert(1, parentdir)
# print("currentdir = ", currentdir)
# print("parentdir=", parentdir)

-2

真是一场激烈的辩论!

我是Python的新手(但有多年编程经验,不喜欢Perl),对于Apache设置这个黑魔法还是相对外行,但我知道我需要什么(我认为)才能让我的小实验项目在家里运行。

以下是我对情况的总结:

如果我使用-m 'module'方法,我需要:

  1. 将所有内容点在一起;
  2. 从父文件夹运行它;
  3. 去掉“.py”;
  4. 在每个子文件夹中创建一个空的(!) __init__.py文件。

CGI环境中,当我将脚本目录别名化并想要直接运行脚本作为/dirAlias/cgi_script.py时,该怎么办呢?

为什么修改 sys.path 被称为黑客行为?Python文档页面中指出:"程序可以自由地修改此列表来满足其自身目的。"如果它起作用了,那就没问题了,对吧?账户部门的会计并不关心它是如何工作的。
我只想进入上一级目录并进入“模块”目录:just
.../py
      /cgi
      /build
      /modules

所以我的“模块”可以从CGI世界或服务器世界导入。

我已经尝试过-m/modules方法,但我更喜欢以下方法(并且不会混淆如何在CGI空间中运行):

  1. /path/to/python/Lib目录(或默认的sys.path列表中的任何其他目录)中创建XX_pathsetup.py文件。'XX'是一些标识符,它声明了根据文件中的规则设置路径的意图。

  2. 在任何想要能够从上述目录配置的“模块”目录中导入的脚本中,只需使用import XX_pathsetup.py即可。

这是我的非常简单的XX_pathsetup.py文件:

import sys, os
pypath = sys.path[0].rsplit(os.sep, 1)[0]
sys.path.insert(0, pypath + os.sep + 'modules')

在我看来,这不是一种“黑客”行为。这只是将一个小文件放入Python“Lib”目录中,并使用一个导入语句声明意图修改路径搜索顺序。


这个答案在很多方面都是错误的。自从Python 3.3以来,__init__.py文件已经不再需要用于标记包,并且它们的目的完全不同。它们不需要为空;只是创建一个空文件比创建一个非空文件更简单。除此之外,请记住这不是一个讨论论坛,期望答案直接切入主题,不要谈论个人经验或你尝试解决的相关问题(请阅读[答案])。最后,为什么你在2023年还在直接使用CGI? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接