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

如何在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)


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

问题是,通过将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开关运行包中的模块。


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复制到目标系统的脚本文件夹。


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

    我以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)

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


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

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


    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')

    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

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


    用实例说明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"函数,但是如果已经定义了一个模块,这就行不通了(我发现它的行为非常奇怪)。


    让我把这个放在这里,作为我自己的参考。我知道这不是一个好的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


    来自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是"全球性的",但它还没有给我带来麻烦。


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

    1
    import sub2.mod2

    而不是

    1
    from ..sub2 import mod2


    推荐阅读