相对进口量为十亿次


716

我来过这里:

以及很多我没有复制的URL,有些在SO上,有些在其他网站上,当我以为我很快就会找到解决方案时。

永远存在的问题是:在Windows 7、32位Python 2.7.3中,如何解决此“尝试以非软件包方式进行相对导入”消息?我在pep-0328上构建了该软件包的精确副本:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

导入是从控制台完成的。

我确实在相应的模块中创建了名为垃圾邮件和鸡蛋的函数。自然,它不起作用。答案显然是在我列出的第4个网址中,但对我来说都是校友。我访问的其中一个URL上有此响应:

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

上面的回答看起来很有希望,但对我来说,全都是象形文字。所以我的问题是,如何使Python不返回“未包装的相对导入尝试”?可能有一个涉及-m的答案。

有人可以告诉我为什么Python会给出该错误消息,“非包装”的含义,为什么以及如何定义“包装”以及准确的答案,这些措辞足以使幼儿园的学生理解


5
您如何尝试使用显示的文件?您正在运行的代码是什么?
BrenBarn

参见python.org/dev/peps/pep-0328。我使用了我在帖子中描述的包格式。在初始化 .py文件是空的。moduleY.py有def spam(): pass,moduleA.py有def eggs(): pass。我试图执行几个“从.something导入内容”命令,但是它们不起作用。同样,请参阅pep-0328。

6
看我的答案。您仍然没有完全弄清楚自己在做什么,但是如果您尝试from .something import something在交互式解释器中进行操作,那将无法正常工作。相对导入只能在模块内使用,而不能交互使用。
BrenBarn

105
光是“数十亿”的人(在本评论中为83,136人),在进口方面就很难找到这个问题;我们只能得出结论,即使不是大多数程序员,python导入对于许多程序员来说也是违反直觉的。Guido,也许您应该接受这一要求,并要求委员会重新设计进口机制。至少,如果x.py和z.py位于同一目录中,则此语法应该起作用。也就是说,如果x.py具有以下语句,则“ from .z import MyZebraClass”如果以main身份运行,x应该也应导入z !为什么这么难?
史蒂夫·L

4
阅读完该线程的大部分内容后,尽管不能回答问题,但“仅使用绝对导入”似乎是解决方案……
CodeJockey

Answers:


1043

脚本与模块

这是一个解释。简短的版本是直接运行Python文件与从其他位置导入该文件之间存在很大差异。 仅知道文件位于哪个目录并不能确定Python认为位于哪个软件包。 此外,这还取决于您如何通过运行或导入将文件加载到Python中。

加载Python文件的方式有两种:作为顶级脚本或作为模块。如果直接执行文件(例如,python myfile.py在命令行上键入),则将文件作为顶级脚本加载。如果您这样做python -m myfile,则将其作为模块加载,或者import在其他文件中遇到语句时将其加载。一次只能有一个顶级脚本。顶层脚本是您为了开始而运行的Python文件。

命名

加载文件时,将为其指定一个名称(存储在其__name__属性中)。如果已将其作为顶级脚本加载,则其名称为__main__。如果将其作为模块加载,则其名称为文件名,其后是其所属的所有软件包/子软件包的名称,并用点号分隔。

例如,在您的示例中:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

如果您导入moduleX(请注意:imported,不直接执行),则其名称为package.subpackage1.moduleX。如果导入moduleA,则名称为package.moduleA。但是,如果直接从命令行运行 moduleX,则名称为__main__,如果直接从命令行运行moduleA,则名称为__main__。当模块作为顶级脚本运行时,它将失去其常规名称,而其名称改为__main__

不通过其包含的包访问模块

还有一个额外的问题:模块的名称取决于它是从其所在目录“直接”导入还是通过软件包导入。仅当您在目录中运行Python并尝试将文件导入同一目录(或其子目录)时,这才有所不同。例如,如果您在目录中启动Python解释器package/subpackage1,然后执行do import moduleX,则其名称moduleX将仅为moduleX,而不是package.subpackage1.moduleX。这是因为Python在启动时会将当前目录添加到其搜索路径中。如果它在当前目录中找到了要导入的模块,则不会知道该目录是软件包的一部分,并且软件包信息也不会成为模块名称的一部分。

一种特殊情况是,如果您以交互方式运行解释器(例如,只需键入python并开始即时输入Python代码)。在这种情况下,该交互式会话的名称为__main__

现在,这是您的错误消息的关键所在:如果模块的名称没有点,则不认为它是包的一部分。文件实际在磁盘上的哪个位置都没有关系。重要的是它的名称是什么,它的名称取决于您如何加载它。

现在查看您在问题中包含的报价:

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

相对进口...

相对导入使用模块的名称来确定模块在包中的位置。当您使用类似的相对导入时from .. import foo,点表示在包层次结构中增加了一些级别。例如,如果您当前模块的名称为package.subpackage1.moduleX,则..moduleA表示package.moduleA。为了使a from .. import起作用,模块的名称必须至少包含与import语句中一样多的点。

...只是相对的

但是,如果模块的名称为__main__,则不认为它在软件包中。它的名称没有点,因此您不能from .. import在其中使用语句。如果您尝试这样做,则会收到“非包中的相对导入”错误。

脚本无法导入相对

您可能所做的是尝试从命令行运行moduleX等。执行此操作时,其名称设置为__main__,这意味着其中的相对导入将失败,因为它的名称不会显示它在软件包中。请注意,如果您从模块所在的同一目录运行Python,然后尝试导入该模块,也会发生这种情况,因为如上所述,Python会“过早”在当前目录中找到该模块,而没有意识到它是包装的一部分。

还请记住,当您运行交互式解释器时,该交互式会话的“名称”始终为__main__。因此,您不能直接从交互式会话进行相对导入。相对导入仅在模块文件中使用。

两种解决方案:

  1. 如果您确实确实想moduleX直接运行,但是仍然希望将其视为软件包的一部分,则可以这样做python -m package.subpackage1.moduleX。该命令-m告诉Python将其作为模块而不是顶级脚本进行加载。

  2. 或者,也许您实际上并不想运行 moduleX,而只想运行其他脚本,例如myfile.py,该脚本使用 inside函数moduleX。如果是这样的话,把myfile.py 其他地方 - 没有内部package目录-并运行它。如果myfile.py您在内部执行类似的操作from package.moduleA import spam,则效果很好。

笔记

  • 对于这两种解决方案,都package必须可以从Python模块搜索路径(sys.path)访问包目录(在您的示例中)。如果不是,您将根本无法可靠地使用包装中的任何物品。

  • 从Python 2.6开始,用于程序包解析的模块的“名称”不仅由其__name__属性确定,而且由__package__属性确定。这就是为什么我避免使用显式符号__name__来引用模块的“名称”的原因。因为Python 2.6模块的“名”是有效的__package__ + '.' + __name__,或者只是__name__如果__package__None)。


62
Its name has no dots, and therefore you cannot use from .. import statements inside it. If you try to do so, you will get the "relative-import in non-package" error.这从根本上令人不安。查看当前目录有什么困难?Python应该能够做到这一点。这个问题在3x版中已解决吗?

7
@Stopforgettingmyaccounts ...:PEP 366显示了它的工作原理。在文件内部,您可以执行__package__ = 'package.subpackage1'类似操作。这样即使直接运行该文件,始终将其视为该软件包的一部分。如果您还有其他问题,__package__我们可能会在此处提出您的原始问题,可能需要再提出一个问题。
BrenBarn

108
这应该是所有Python相对导入问题的答案。甚至应该在文档中。
edsioufi 2014年

10
参见python.org/dev/peps/pep-0366- “请注意,仅当已经可以通过sys.path访问顶层程序包时,此样板才足够。直接执行将需要操纵sys.path的其他代码。在顶层软件包无法导入的情况下工作。” -这对我来说是最令人不安的一点,因为这个“附加代码”实际上很长,不能存储在包中的其他位置以便轻松运行。
Michael Scott Cuthbert 2015年

14
目前,该答案基于有关__name__和的一些重要细节sys.path。具体而言,与python -m pkg.mod__name__被设定为__main__,不pkg.mod; 相对进口是使用__package__而不是__name__在这种情况下解决的。另外,Python sys.path在运行时将脚本的目录而不是当前目录添加到该目录python path/to/script.pysys.path在运行大多数其他方式(包括)时,它将当前目录添加到该目录python -m pkg.mod
user2357112支持Monica

42

这确实是python中的问题。混淆的根源是人们错误地将相对进口作为相对的进口,而不是。

例如,当您在faa.py中编写时:

from .. import foo

这具有只有一个意思faa.py识别并加载由蟒,在执行期间,作为一个包的一部分。在这种情况下,该模块的名称faa.py将是例如some_packagename.faa。如果仅由于文件在当前目录中而被加载,则在运行python时,其名称将不会引用任何软件包,最终相对导入将失败。

引用当前目录中模块的一个简单解决方案是使用以下方法:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo

5
正确的解决方案是 from __future__ import absolute_import并强迫用户正确使用您的代码...以便您始终可以这样做from . import foo
Giacomo Alzetta

@Giacomo:我的问题的绝对正确答案。谢谢!
法比奥

8

这是一个通用的配方,经过修改以适合作为示例,我现在使用它来处理以程序包形式编写的Python库,其中包含相互依赖的文件,我希望能够逐个测试其中的某些部分。让我们称之为lib.foo它,它需要lib.fileA对函数f1f2lib.fileB类进行访问Class3

我打了几个print电话,以帮助说明这是如何工作的。实际上,您可能希望将其删除(也许还删除该from __future__ import print_function行)。

这个特定的例子太简单了,无法显示何时确实需要在中插入条目sys.path。(见拉尔斯的回答为我们的情况下,需要它,当我们有包目录中的两个或两个以上的水平,然后我们使用os.path.dirname(os.path.dirname(__file__))-但它并没有真正伤害在这里无论是。)它也足够安全要做到这一点,而不if _i in sys.path测试。但是,如果每个导入文件插入相同的路径-例如,如果两个fileAfileB希望导入实用程序从包中,这个杂波了sys.path具有相同路径很多次,所以很高兴有if _i not in sys.path在样板。

from __future__ import print_function # only when showing how this works

if __package__:
    print('Package named {!r}; __name__ is {!r}'.format(__package__, __name__))
    from .fileA import f1, f2
    from .fileB import Class3
else:
    print('Not a package; __name__ is {!r}'.format(__name__))
    # these next steps should be used only with care and if needed
    # (remove the sys.path manipulation for simple cases!)
    import os, sys
    _i = os.path.dirname(os.path.abspath(__file__))
    if _i not in sys.path:
        print('inserting {!r} into sys.path'.format(_i))
        sys.path.insert(0, _i)
    else:
        print('{!r} is already in sys.path'.format(_i))
    del _i # clean up global name space

    from fileA import f1, f2
    from fileB import Class3

... all the code as usual ...

if __name__ == '__main__':
    import doctest, sys
    ret = doctest.testmod()
    sys.exit(0 if ret.failed == 0 else 1)

这里的想法是这样的(请注意,这些在python2.7和python 3.x中的功能都相同):

  1. 如果从普通代码导入为常规软件包import libfrom lib import foo作为常规软件包运行,__package则is lib__name__is lib.foo。我们采用第一个代码路径,从.fileA等导入。

  2. 如果运行为python lib/foo.py__package__则将为None且__name__将为__main__

    我们采用第二条代码路径。该lib目录已经存在,sys.path因此无需添加它。我们从fileA等导入

  3. 如果在lib目录中以身份运行python foo.py,则其行为与情况2相同。

  4. 如果在libas目录中运行python -m foo,其行为类似于情况2和3。但是,lib目录的路径不在in中sys.path,因此我们在导入之前将其添加。如果我们先运行Python然后运行,则同样适用import foo

    (由于. sys.path,我们并不真正需要添加此路径的绝对的版本。这是一个更深层次的包嵌套结构,我们想要做的from ..otherlib.fileC import ...,有差别。如果你不这样做,就可以在sys.path完全省略所有操作。)

笔记

仍然有一个怪癖。如果从外部运行整个过程:

$ python2 lib.foo

要么:

$ python3 lib.foo

行为取决于的内容lib/__init__.py。如果存在并且为空,则一切正常:

Package named 'lib'; __name__ is '__main__'

但是,如果lib/__init__.py 本身导入,routine以便可以routine.name直接将导出为lib.name,则会得到:

$ python2 lib.foo
Package named 'lib'; __name__ is 'lib.foo'
Package named 'lib'; __name__ is '__main__'

也就是说,该模块两次导入,一次是通过包导入的,然后是再次导入的,__main__以便它运行您的main代码。Python 3.6及更高版本对此发出警告:

$ python3 lib.routine
Package named 'lib'; __name__ is 'lib.foo'
[...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules
after import of package 'lib', but prior to execution of 'lib.foo';
this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Package named 'lib'; __name__ is '__main__'

警告是新的,但警告说,有关的行为是不能。这就是所谓的双重导入陷阱的一部分。(有关其他详细信息,请参见问题27487。)尼克·科格兰(Nick Coghlan)说:

下一个陷阱存在于所有当前的Python版本(包括3.3)中,并且可以在以下常规准则中进行总结:“切勿将包目录或包内的任何目录直接添加到Python路径中”。

请注意,虽然此处违反了该规则,但不将要加载的文件作为程序包的一部分加载时才这样做,并且我们的修改经过专门设计,允许我们访问该程序包中的其他文件。(而且,正如我所指出的,我们可能根本不应该为单层程序包执行此操作。)如果我们想变得更加干净,可以将其重写为例如:

    import os, sys
    _i = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    if _i not in sys.path:
        sys.path.insert(0, _i)
    else:
        _i = None

    from sub.fileA import f1, f2
    from sub.fileB import Class3

    if _i:
        sys.path.remove(_i)
    del _i

也就是说,我们进行了sys.path足够长的修改以实现导入,然后将其恢复原样(_i如果且仅当我们添加的一个副本时,删除一个副本_i)。


7

因此,在与其他许多人讨论了这一问题之后,我遇到了Dorian B本文中发布的注释,该注释解决了我在开发与Web服务一起使用的模块和类时遇到的特定问题,但我也想成为能够使用PyCharm中的调试器工具在编写代码时对其进行测试。要在独立的类中运行测试,我将在类文件末尾包含以下内容:

if __name__ == '__main__':
   # run test code here...

但是如果我想在同一文件夹中导入其他类或模块,则必须将所有导入语句从相对符号更改为本地引用(即,删除点(。)。)但是在阅读了多里安的建议之后,我尝试了他的“一线”,它的工作!现在,我可以在PyCharm中进行测试,并在另一个被测类中使用该类时,或者在Web服务中使用该类时,将测试代码保留在原位!

# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
    from codex import Codex # these are in same folder as module under test!
    from dblogger import DbLogger
else:
    from .codex import Codex
    from .dblogger import DbLogger

if语句检查是否将这个模块作为main运行,或者是否在另一个被测试为main的模块中使用。也许这很明显,但是我在这里提供此说明,以防其他因上述相对导入问题而感到沮丧的人可以使用它。


1
这实际上解决了。但这真的很讨厌。为什么这不是默认行为?!
lo tolmencre

4

这是我不建议使用的一种解决方案,但在某些情况下可能根本不生成模块,这可能会很有用:

import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/your_dir")
import your_script
your_script.a_function()

2

我有一个类似的问题,我不想更改Python模块的搜索路径,而需要从脚本中相对地加载模块(尽管“脚本不能与所有对象相对地导入”,正如BrenBarn上面很好地解释的那样)。

因此,我使用了以下技巧。不幸的是,它依赖于imp从3.4版本开始就弃用的模块,而被取而代之importlib。(这是否也可以用importlib?我不知道。)不过,这种破解现在仍然有效。

从位于文件夹中的脚本访问moduleXin的成员的示例:subpackage1subpackage2

#!/usr/bin/env python3

import inspect
import imp
import os

def get_script_dir(follow_symlinks=True):
    """
    Return directory of code defining this very function.
    Should work from a module as well as from a script.
    """
    script_path = inspect.getabsfile(get_script_dir)
    if follow_symlinks:
        script_path = os.path.realpath(script_path)
    return os.path.dirname(script_path)

# loading the module (hack, relying on deprecated imp-module)
PARENT_PATH = os.path.dirname(get_script_dir())
(x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1'])
module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc)

# importing a function and a value
function = module_x.my_function
VALUE = module_x.MY_CONST

较干净的方法似乎是修改Federico提到的用于加载模块的sys.path。

#!/usr/bin/env python3

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    # __file__ should be defined in this case
    PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
   sys.path.append(PARENT_DIR)
from subpackage1.moduleX import *

看起来更好...太糟糕了,它仍然需要您在文件中嵌入父目录的名称...也许可以使用importlib进行改进。也许甚至可以对importlib进行猴子补丁处理,以使相对导入对于简单的用例来说“有效”。我会解决的。
Andrew Wagner

我正在使用python 2.7.14。这样的事情仍然有效吗?
user3474042 '02

我刚刚在python 2.7.10上测试了这两种方法,它们对我来说很好用。如果确实如此,您将不会遇到2.7中不推荐使用的imp模块的问题,因此一切都更好。
拉斯,

2

__name__ 更改取决于所讨论的代码是在全局名称空间中运行还是作为导入模块的一部分运行。

如果代码不在全局空间中运行,则为__name__模块名称。如果它在全局名称空间中运行-例如,如果您将其输入到控制台中,或者使用python.exe yourscriptnamehere.pythen __name__成为脚本来运行该模块"__main__"

您将看到很多if __name__ == '__main__'用于测试是否从全局名称空间运行代码的python代码 -这使您可以拥有一个兼用作脚本的模块。

您是否尝试过从控制台进行这些导入?


嗯,所以你提到-m。这使得您的模块可以作为脚本执行-如果在其中粘贴if __name__ =='__main__',则应该看到它是'__main__',这是因为-m所致。尝试仅将模块导入另一个模块,因此它不是顶层……应该允许您进行相对导入
theodox 2013年

我尝试从控制台进行这些导入,活动文件是正确的模块。

@Stopforgettingmyaccounts ...:“活动文件”是什么意思?
BrenBarn

我使用Pyscripter。运行这些导入时,我位于moduleX.py中:从.moduleY导入垃圾邮件从。导入ModuleY。

不导入.moduleY后跟moduleY.spam()吗?
theodox

2

@BrenBarn的回答说明了一切,但是如果您像我一样,可能需要一段时间才能理解。这是我的情况,以及@BrenBarn的答案如何适用于此,也许会对您有所帮助。

案子

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

使用我们熟悉的示例,并添加moduleX.py与..moduleA的相对导入。假设我尝试在导入moduleX的subpackage1目录中编写测试脚本,但随后得到了OP描述的可怕错误。

将测试脚本移至与package相同的级别,然后导入package.subpackage1.moduleX

说明

如前所述,相对导入是相对于当前名称进行的。当我的测试脚本从同一目录导入moduleX时,moduleX内的模块名称为moduleX。当遇到相对导入时,解释器无法备份程序包层次结构,因为它已经位于顶部

当我从上方导入moduleX时,moduleX内部的名称为package.subpackage1.moduleX,并且可以找到相对的导入


希望你能指导我。在下面的链接中,如果转到案例3,则表明无法使用解决方案1。请您检查一下并告诉我。这将极大地帮助我。chrisyeh96.github.io/2017/08/08/…–
变量

@variable链接中有错字,并且我无法编辑。看了案例3,并没有完全按照您的意思。当我在python 2中尝试该示例时,没有问题让我觉得我错过了一些东西。也许您应该发布一个新问题,但需要提供一个更清晰的示例。案例4个润色我谈论这里我的答案是什么:你不能去了一个目录的相对进口,除非父目录解释开始
布拉德·德瑞

谢谢,我指的是python 3,在这里是问题stackoverflow.com/questions/58577767/…–
变量

1

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

在PyPi上编写了一些python程序包,它可能会对这个问题的查看者有所帮助。如果一个人希望能够运行一个python文件,而该文件包含一个包/项目中的包含上层包的导入文件,而又不直接位于导入文件的目录中,则该包文件可以作为解决方法。https://pypi.org/project/import-anywhere/


-2

为了使Python不再返回我“尝试以非包方式进行相对导入”。包/

init .py subpackage1 / init .py moduleX.py moduleY.py subpackage2 / init .py moduleZ.py moduleA.py

仅当您将相对导入应用于父文件时,才会发生此错误。例如,在moduleA.py中对“ print(name)”进行编码后,父文件已经返回main,因此该文件已经是main它无法进一步返回任何父包。包subpackage1和subpackage2的文件中需要相对导入,您可以使用“ ..”来引用父目录或模块。但是parent是如果已经是顶级程序包,则它不能在该父目录(程序包)之上。您向父母应用相对导入的此类文件只能与绝对导入应用一起使用。如果您使用绝对导入到父程序包中将不会出现错误,因为PYTHON PATH的概念定义了项目的顶层,即使您的文件位于子程序包中,python也会知道谁在程序包的顶层

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.