it-swarm.cn

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

想象一下这个目录结构:

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

我正在编码mod1,我需要从mod2导入一些东西。我该怎么办?

我尝试了from ..sub2 import mod2但是我得到了“非包装中尝试相对导入”。

我google了一下,但发现只有“sys.path操纵”黑客。有没有干净的方式?


编辑:我所有的__init__.py目前都是空的

Edit2:我正在尝试这样做,因为sub2包含跨子包共享的类(sub1subX等)。

编辑3:我正在寻找的行为与 PEP 366中描述的相同 (感谢John B)

483
Joril

每个人似乎都想告诉你你应该做什么,而不仅仅是回答这个问题。

问题是你通过将mod1.py作为参数传递给解释器来运行模块'__main__'。

PEP 328

相对导入使用模块的__name__属性来确定模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,它设置为'__main__'),则解析相对导入,就像模块是顶级模块一样,无论模块实际位于文件系统的哪个位置。

在Python 2.6中,他们添加了相对于主模块引用模块的功能。 PEP 366 描述了变化。

更新 :根据Nick Coghlan的说法,推荐的替代方法是使用-m开关在包内运行模块。

303
John B
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. 你运行python main.py
  2. main.pyimport app.package_a.module_a
  3. module_a.py执行import app.package_b.module_b

或者2或3可以使用:from app.package_a import module_a

只要你的PYTHONPATH中有app,那就行了。 main.py可以在任何地方。

因此,您编写setup.py将整个应用程序包和子包复制(安装)到目标系统的python文件夹,并将main.py复制(安装)到目标系统的脚本文件夹。

114
nosklo

这是适用于我的解决方案:

我做相对导入为from ..sub2 import mod2然后,如果我想运行mod1.py然后我转到app的父目录并使用python -m开关作为python -m app.sub1.mod1运行模块。

相对导入出现此问题的真正原因是相对导入通过获取模块的__name__属性来工作。如果直接运行模块,则__name__设置为__main__,并且它不包含有关包结构的任何信息。而且,这就是为什么python抱怨relative import in non-package错误。

因此,通过使用-m开关,您可以向python提供包结构信息,通过它可以成功解析相对导入。

在进行相对导入时,我多次遇到过这个问题。并且,在阅读了之前的所有答案之后,我仍然无法以干净的方式弄清楚如何解决它,而无需在所有文件中放置样板代码。 (虽然有些评论非常有用,感谢@ncoghlan和@XiongChiamiov)

希望这可以帮助那些与相对进口问题作斗争的人,因为通过PEP真的不好玩。

113
Pankaj

“Guido将包中的脚本视为反模式”(拒绝 PEP-3122

我花了很多时间试图找到解决方案,在Stack Overflow上阅读相关帖子并对自己说“必须有更好的方法!”。看起来没有。

46
lesnik

这是100%解决的:

  • 应用/
    • main.py
  • 设置/
    • local_setings.py

在app/main.py中导入设置/ local_setting.py:

main.py:

import sys
sys.path.insert(0, "../settings")


try:
    from local_settings import *
except ImportError:
    print('No Import')
27
def import_path(fullpath):
    """ 
    Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
    """
    path, filename = os.path.split(fullpath)
    filename, ext = os.path.splitext(filename)
    sys.path.append(path)
    module = __import__(filename)
    reload(module) # Might be out of date
    del sys.path[-1]
    return module

我正在使用此代码段从路径导入模块,希望有所帮助

24
iElectric

用例子解释nosklo's回答

注意:所有__init__.py文件都是空的。

main.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       fun_a.py
    package_b/ ->
       __init__.py
       fun_b.py

应用程序/ package_a/fun_a.py

def print_a():
    print 'This is a function in dir package_a'

应用程序/ package_b/fun_b.py

from app.package_a.fun_a import print_a
def print_b():
    print 'This is a function in dir package_b'
    print 'going to call a function in dir package_a'
    print '-'*30
    print_a()

main.py

from app.package_b import fun_b
fun_b.print_b()

如果你运行$ python main.py它返回:

This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
  • main.py:from app.package_b import fun_b
  • fun_b.py做from app.package_a.fun_a import print_a

所以package_b文件夹中的文件使用了package_a文件夹中的文件,这就是你想要的。对??

21
suhailvs

不幸的是,这是一个sys.path hack,但它运行得很好。

我在另一层遇到了这个问题:我已经有了一个指定名称的模块,但它是错误的模块。

我想做的是以下(我正在使用的模块是module3):

mymodule\
   __init__.py
   mymodule1\
      __init__.py
      mymodule1_1
   mymodule2\
      __init__.py
      mymodule2_1


import mymodule.mymodule1.mymodule1_1  

请注意,我已经安装了mymodule,但在我的安装中我没有“mymodule1”

我会得到一个ImportError,因为它试图从我安装的模块导入。

我试图做一个sys.path.append,但是没有用。什么工作是 sys.path.insert

if __== '__main__':
    sys.path.insert(0, '../..')

这样的黑客,但让一切工作!所以请记住,如果你想让你的决定 覆盖其他路径 那么你需要使用sys.path.insert(0,pathname)来使它工作!这对我来说是一个非常令人沮丧的问题,很多人都说使用sysndpath的“append”函数,但是如果你已经定义了一个模块(我发现它非常奇怪的行为),这不起作用

11
Garrett Berg

我想把它放在这里供我自己参考。我知道它不是很好的Python代码,但我需要一个脚本用于我正在处理的项目,我想将脚本放在scripts目录中。

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
10
milkypostman

正如@EvgeniSergeev在对OP的评论中所说,您可以使用以下任意位置从.py文件导入代码:

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

这取自 this SO answer

8
LondonRob
4
mossplix

除了John B所说的,似乎设置__package__变量应该有所帮助,而不是改变__main__,这可能会搞砸其他事情。但就我的测试而言,它并没有完全发挥作用。

我有同样的问题,PEP 328或366都没有完全解决问题,因为在一天结束时,两者都需要将包的头部包含在sys.path中,据我所知。

我还要提一下,我没有找到如何格式化应该进入这些变量的字符串。是"package_head.subfolder.module_name"还是什么?

1
Gabriel

我发现将“PYTHONPATH”环境变量设置到顶部文件夹更容易:

bash$ export PYTHONPATH=/PATH/TO/APP

然后:

import sub1.func1
#...more import

当然,PYTHONPATH是“全球性的”,但它并没有给我带来麻烦。

1
Andrew_1510