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

时间:2020-03-05 18:56:05  来源:igfitidea点击:

想象一下这个目录结构:

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

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

我从..sub2 import mod2中尝试过,但是却遇到了"未打包的相对导入尝试"。

我四处搜寻,但只发现"sys.path操纵"的黑子。有没有一种干净的方法?

编辑:我所有的__init__.py当前为空

Edit2:我正在尝试执行此操作,因为sub2包含在子包之间共享的类(sub1,subX等)。

Edit3:我要查找的行为与PEP 366中描述的相同(感谢John B)

解决方案

回答

我认为我们必须问自己:

  • 为什么我需要这样做?
  • 我的包裹分离做得好吗?

我不知道背景为何我们要这样做。但是对我来说,更简洁的设计将是具有以下包装结构:

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

然后,我们只需要执行以下操作:

from sub12 import mod2

回答

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

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

从PEP 328:

Relative imports use a module's __name__ attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to '__main__') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.

在Python 2.6中,他们添加了相对于主模块引用模块的功能。 PEP 366说明了更改。

更新:根据Nick Coghlan的建议,推荐的替代方法是使用-m开关运行软件包中的模块。

回答

main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  • 我们运行python main.py
  • main.py可以执行以下操作:import app.package_a.module_a`
  • module_a.py确实导入app.package_b.module_b

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

只要我们在PYTHONPATH中具有" app",它就可以工作。 main.py可能在那时的任何地方。

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

回答

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

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