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

How to do relative imports in Python?

想象一下这个目录结构:

1
2
3
4
5
6
7
8
app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

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

我尝试过from ..sub2 import mod2,但我得到了一个"尝试的非包装相对进口"。

我到处搜索,但只发现"sys.path操纵"黑客。没有干净的路吗?

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

edit2:我之所以这么做,是因为sub2包含跨子包共享的类(sub1subX等)。

伊迪丝3:我要找的行为和PEP 366中描述的一样(谢谢约翰B)

  • 我建议您更新您的问题,以便更清楚地说明您正在描述PEP 366中解决的问题。
  • 这是一个冗长的解释,但请看这里:stackoverflow.com/a/10713254/1267156我回答了一个非常相似的问题。直到昨晚我都有同样的问题。
  • 对于希望加载位于任意路径的模块的用户,请参见:stackoverflow.com/questions/67631/…
  • 在相关注释中,python 3将默认情况下将导入的默认处理更改为绝对处理;必须显式指定相对导入。


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

问题是,通过将mod1.py作为参数传递给解释器,您将模块作为"主"运行。

来自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.

在Python2.6中,他们添加了相对于主模块引用模块的能力。PEP 366描述了变化。

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

  • 这仍然不起作用:@
  • 这里的答案涉及在程序的每个入口点处理sys.path。我想这是唯一的办法。
  • 建议的替代方法是使用-m开关在包内运行模块,而不是直接指定它们的文件名。
  • @军士长:谢谢。你的评论应该是这个问题的答案。
  • 我不明白:答案在哪里?如何在这样的目录结构中导入模块?
  • @汤姆:在这种情况下,mod1可以用于cx1〔1〕。然后,从应用程序内部运行mod1,执行python -m sub1.mod1
  • @熊基亚米奥夫:这是否意味着如果你的python嵌入到一个应用程序中,你就不能这样做,所以你不能访问python命令行开关?
  • @Mattjoiner:如果你运行mod1.py作为python -m app.sub1.mod1(来自app的父目录),就像pankaj写的那样。
  • 为了说明这一点,当您在脚本中执行from .m import whatever操作时,作为一个参数传递给解释器,它基本上解析为from __main__.m import whatever。这使得它寻找__main__/m.py
  • "每个人似乎都想告诉你应该做什么,而不仅仅是回答问题。"好的,很好。这就是帮助编写软件的方法!当我面对良好的训练时,我想知道。


1
2
3
4
5
6
7
8
9
10
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.pyimport app.package_b.module_b吗?
  • 或者2或3可以使用:from app.package_a import module_a

    只要你的肾盂里有一个app就行了。那时,main.py可能在任何地方。

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

    • 回答得很好。如果不在pythonpath中安装包,有没有什么方法可以这样导入?
    • 建议阅读:blog.habnab.it/blog/2013/07/21/python-packages-and-you
    • 然后,有一天,需要更改应用程序的名称来测试应用程序。会发生什么?您需要更改所有源代码,导入app.package_b.module_b-->test_app.package_b.module_b。这绝对是一个糟糕的做法…我们应该尝试在包中使用相对导入。


    以下是适合我的解决方案:

    我以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提供包结构信息,通过这些信息,python可以成功地解析相对导入。

    在进行相对导入时,我多次遇到此问题。而且,在阅读了所有之前的答案之后,我仍然无法在不需要将样板代码放入所有文件的情况下,以一种干净的方式解决它。(尽管有些评论真的很有帮助,多亏了@ncoghlan和@xiongchiamiov)

    希望这能帮助那些正与进口问题作斗争的人,因为经历政治公众人物真的不好玩。

    • 最佳答案imho:不仅解释了op为什么会有这个问题,而且还找到了一种不改变模块导入方式的方法来解决这个问题。毕竟,欧佩克的相对进口量还不错。罪魁祸首是直接作为脚本运行时无法访问外部包,这是-m设计用来解决的问题。
    • 还要注意:这个答案是在问题5年后得出的。这些功能当时不可用。
    • 如果要从同一目录导入模块,可以执行from . import some_module


    "guido将包内运行脚本视为反模式"(已拒绝)PEP-3122)

    我花了这么多时间试图找到一个解决方案,阅读了栈溢出的相关文章,并对自己说:"一定有更好的方法!"好像没有。

    • 注:已经提到的PEP-366(与PEP-3122同时创建)提供了相同的功能,但使用了不同的向后兼容实现,即,如果您想在包内运行一个模块作为脚本,并在其中使用显式的相对导入,则可以使用-m开关:python -m app.sub1.mod1或调用from sub2 import mod2来运行它。0]来自顶级脚本(例如,从setuptools的entry_点(在setup.py中定义)生成)。


    100%解决:

    • APP/
      • Me.Py
    • 设置/
      • 本地设置.py

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

    MY.PY:

    1
    2
    3
    4
    5
    6
    7
    8
    import sys
    sys.path.insert(0,"../settings")


    try:
        from local_settings import *
    except ImportError:
        print('No Import')

    • 谢谢您!所有的PPL都强迫我以不同的方式运行我的脚本,而不是告诉我如何在脚本中解决它。但是我不得不修改代码来使用sys.path.insert(0,"../settings"),然后使用from local_settings import *


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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

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

    • 我正在使用这个片段,结合IMP模块(如本文[1]所述),效果非常好。[1]:stackoverflow.com/questions/1096216/…
    • 可能,sys.path.append(path)应替换为sys.path.insert(0,path),sys.path[-1]应替换为sys.path[0]。否则,如果搜索路径中已经存在同名模块,则函数将导入错误的模块。例如,如果当前目录中有"some.py",则import_path("/imports/some.py")将导入错误的文件。
    • 我同意!有时,其他相关的进口产品也会产生影响。使用sys.path.insert
    • 您将如何从x import y(或*)复制的行为?
    • 不清楚,请指定此脚本的完整用法以解决操作问题。


    用实例说明nosklo's答案

    注:所有__init__.py文件均为空。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    main.py
    app/ ->
        __init__.py
        package_a/ ->
           __init__.py
           fun_a.py
        package_b/ ->
           __init__.py
           fun_b.py

    应用程序/软件包

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

    应用程序/软件包

    1
    2
    3
    4
    5
    6
    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()

    Me.Py

    1
    2
    from app.package_b import fun_b
    fun_b.print_b()

    如果运行$ python main.py,则返回:

    1
    2
    3
    4
    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
    • 有趣的是EDOCX1[12]

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


    不幸的是,这是一个sys.path黑客,但它工作得很好。

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

    我想做的是如下(我工作的模块是模块3):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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

    1
    2
    if __name__ == '__main__':
        sys.path.insert(0, '../..')

    真是一个黑客,但一切都正常!所以请记住,如果您希望自己的决定覆盖其他路径,那么您需要使用sys.path.insert(0,pathname)使其工作!这对我来说是一个非常令人沮丧的症结,很多人都说要在sys.path中使用"append"函数,但是如果已经定义了一个模块,这就行不通了(我发现它的行为非常奇怪)。

    • sys.path.append('../')对我来说很好(python 3.5.2)
    • 我认为这很好,因为它将黑客定位到可执行文件,并且不会影响其他依赖于您的包的模块。


    让我把这个放在这里,作为我自己的参考。我知道这不是一个好的python代码,但是我需要一个脚本来完成我正在进行的项目,我想把这个脚本放到一个scripts目录中。

    1
    2
    3
    import os.path
    import sys
    sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),"..")))

    正如@evgenisegev在对op的注释中所说,您可以从.py文件中的任意位置导入代码,方法是:

    1
    2
    3
    4
    import imp

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

    这是从这个答案中得出的。


    看看http://docs.python.org/whatsnew/2.5.html pep-328-absolute-and-relative-imports。你可以做到

    1
    from .mod1 import stuff

    • 除了不能从"主"模块中进行相对导入作为John B.States的答案外。


    来自python文档,

    In Python 2.5, you can switch import‘s behaviour to absolute imports using a from __future__ import absolute_import directive. This absolute- import behaviour will become the default in a future version (probably Python 2.7). Once absolute imports are the default, import string will always find the standard library’s version. It’s suggested that users should begin using absolute imports as much as possible, so it’s preferable to begin writing from pkg import string in your code


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

    我也有同样的问题,无论是PEP 328还是366都不能完全解决这个问题,因为在一天结束之前,我都需要把包的头包含在sys.path中,据我所知。

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


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

    1
    bash$ export PYTHONPATH=/PATH/TO/APP

    然后:

    1
    2
    import sub1.func1
    #...more import

    当然,Python是"全球性的",但它还没有给我带来麻烦。

    • 这基本上就是virtualenv允许您管理进口报表的方式。


    假设您在顶层运行,然后在mod1中使用:

    1
    import sub2.mod2

    而不是

    1
    from ..sub2 import mod2