相对进口数十亿次

时间:2013-01-03 03:50:41

标签: python python-2.7 relative import

我来过这里:

还有很多我没有复制过的网址,有些在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

我确实在相应的模块中创建了名为spam和eggs的函数。当然,它没有用。答案显然在我列出的第4个网址中,但这对我来说都是校友。我访问的其中一个网址上有此回复:

  

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

上述反应看起来很有希望,但对我来说都是象形文字。所以我的问题是,如何让Python不回归“尝试非包装中的相对导入”?有一个答案涉及-m,据说。

有人可以告诉我为什么Python会给出错误信息,非包装意味着什么!,为什么以及如何定义'包',确切的答案放就足够容易让幼儿园儿童了解

编辑:导入是从控制台完成的。

12 个答案:

答案 0 :(得分:698)

脚本与模块

这是一个解释。简短版本是直接运行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(注意:导入,未直接执行),则其名称为package.subpackage1.moduleX。如果您导入了moduleA,则其名称将为package.moduleA。但是,如果从命令行直接运行 moduleX,则其名称将改为__main__,如果从命令行直接运行moduleA,则其名称为名称将为__main__。当模块作为顶级脚本运行时,它将丢失其正常名称,而其名称将改为__main__

不通过其包含

访问模块

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

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

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

现在看看你在问题中包含的引用:

  

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

相对进口......

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

...仅在包中相对

但是,如果您的模块名称为__main__,则不会将其视为包中。它的名字没有点,因此你不能在其中使用from .. import语句。如果您尝试这样做,您将获得非包装中的&#34; relative-import&#34;错误。

脚本无法导入相对

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

还要记住,当你运行交互式解释器时,&#34; name&#34;该交互式会话始终是__main__。因此,您不能直接从交互式会话中进行相对导入。相对导入仅用于模块文件中。

两个解决方案:

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

  2. 或许您实际上并不想运行 moduleX,您只想运行一些其他脚本,比如说myfile.pymoduleX中使用函数。如果是这种情况,请将myfile.py 放在其他地方 - 而不是package目录中 - 并运行它。如果在myfile.py内部执行from package.moduleA import spam之类的操作,则可以正常使用。

  3. 备注

    • 对于这些解决方案中的任何一个,必须可以从Python模块搜索路径(package)访问包目录(示例中为sys.path)。如果不是,您将无法可靠地使用包装中的任何东西。

    • 自Python 2.6起,模块的名称&#34;名称&#34;对于包解析目的,不仅取决于其__name__属性,还取决于__package__属性。这就是为什么我避免使用显式符号__name__来引用模块&#34;名称&#34;。从Python 2.6开始,模块就是名称&#34;如果__package__ + '.' + __name____name__,则有效__package__None。)

答案 1 :(得分:20)

This is really a problem within python. The origin of confusion is that people mistakenly takes the relative import as path relative which is not.

For example when you write in faa.py:

from .. import foo

This has a meaning only if faa.py was identified and loaded by python, during execution, as a part of a package. In that case,the module's name for faa.py would be for example some_packagename.faa. If the file was loaded just because it is in the current directory, when python is run, then its name would not refer to any package and eventually relative import would fail.

A simple solution to refer modules in the current directory, is to use this:

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

答案 2 :(得分:4)

这里是一个通用的配方,经过修改以适合作为一个例子,我现在用它来处理作为包编写的Python库,它们包含相互依赖的文件,我希望能够在其中零碎地测试它们的一部分。我们称之为lib.foo,并说它需要lib.fileA的{​​{1}}和f1,以及f2lib.fileB }。

我已经包含了一些Class3次调用,以帮助说明其工作原理。在实践中,您可能希望删除它们(也可能是print行)。

当我们确实需要在from __future__ import print_function中插入一个条目时,这个特殊的例子太简单了。 (当我们需要它时,请参阅Lars' answer,当我们有两个或更多级别的包目录,然后我们使用sys.path - 但它没有&#39;这里真的伤害。)在没有os.path.dirname(os.path.dirname(__file__))测试的情况下,这样做也足够安全。但是,如果每个导入的文件都插入相同的路径 - 例如,如果if _i in sys.pathfileA都要从包中导入实用程序 - 这会使fileB多次混淆相同的路径,所以在样板文件中加上sys.path真是太好了。

if _i not in sys.path

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

  1. 如果以普通代码的常规包导入为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) import libfrom lib import foo__packagelib为{{1} }}。我们采用第一个代码路径,从__name__等导入

  2. 如果以lib.foo运行,.fileA将为无,python lib/foo.py将为__package__

    我们采用第二个代码路径。 __name__目录已经在__main__中,因此无需添加它。我们从lib等导入

  3. 如果在sys.path目录中以fileA运行,则行为与案例2相同。

  4. 如果在lib目录中以python foo.py运行,则行为类似于案例2和3.但是,lib目录的路径不在{{ 1}},所以我们在导入之前添加它。如果我们运行Python然后运行python -m foo,则同样适用。

    (由于lib中的sys.path ,我们并不需要在此处添加路径的绝对版本。这是更深层的包嵌套的地方结构,我们想做import foo,会产生影响。如果你不这样做,你可以完全省略所有.操作。)

  5. 注释

    还有一个怪癖。如果你从外面运行这整件事:

    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代码。 Python 3.6及更高版本警告:

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

    警告是新的,但警告的行为不是。这是有些人称之为the double import trap的一部分。 (有关其他详细信息,请参阅issue 27487。)Nick Coghlan说:

      

    下一个陷阱存在于所有当前版本的Python中,包括3.3,并且可以在以下一般准则中进行总结:&#34;永远不要将包目录或包内的任何目录直接添加到Python路径& #34;

    请注意,虽然我们在此处违反了该规则,但当加载的文件而不是作为包的一部分加载时,我们只执行 ,我们的修改具体是旨在允许我们访问该包中的其他文件。 (并且,正如我所指出的,我们可能根本不应该对单级软件包执行此操作。)如果我们想要更加清洁,我们可能会将其重写为,例如:

    __main__

    也就是说,我们修改main足够长的时间来实现我们的导入,然后将其恢复原样(删除$ 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__' 的一个副本当且仅当我们添加了{{1}的副本时}})。

答案 3 :(得分:3)

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

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

但是,如果我想在同一文件夹中导入其他类或模块,则必须将所有导入语句从相对符号更改为本地引用(即,删除点(。)。)但是在阅读了Dorian的建议之后,我尝试了他的“单线”,它奏效了!现在,我可以在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 进行测试的模块。也许这很明显,但是我在这里提供此注释,以防其他因上述相对导入问题而感到沮丧的人可以使用它。

答案 4 :(得分:2)

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

如果代码未在全局空间中运行,__name__将是模块的名称。如果它在全局命名空间中运行 - 例如,如果您将其键入控制台,或使用python.exe yourscriptnamehere.py将该模块作为脚本运行,则__name__将变为"__main__"

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

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

答案 5 :(得分:2)

这是一个我不推荐的解决方案,但在某些模块根本没有生成的情况下可能会有用:

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

答案 6 :(得分:2)

外语有太多太长的答案。所以我会尽量简短。

如果你写了 from . import module,和你想的相反,module 不会从当前目录导入,而是从包的顶层导入!如果您将 .py 文件作为脚本运行,它根本不知道顶层在哪里,因此拒绝工作。

如果你这样从py -m package.module上面的目录开始package,那么python就知道顶级在哪里了。这与 java 非常相似:java -cp bin_directory package.class

答案 7 :(得分:1)

我有一个类似的问题,我不想改变Python模块搜索 路径和需要从脚本相对加载模块(尽管&#34;脚本无法导入相对于所有&#34; ,因为BrenBarn很好地解释了上文)。

所以我使用了以下hack。不幸的是,它依赖于imp模块 从版本3.4开始被弃用,而不是importlib。 (这也可以用importlib吗?我不知道。)不过,黑客现在还可以。

从驻留在moduleX文件夹中的脚本访问subpackage1中的subpackage2成员的示例:

#!/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

更简洁的方法似乎是修改用于加载模块的sys.path,如Federico所述。

#!/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 *

答案 8 :(得分:0)

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

初始化 .py     分包1 /         初始化 .py         moduleX.py         moduleY.py     分包2 /         初始化 .py         moduleZ.py     moduleA.py

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

答案 9 :(得分:0)

另一个肮脏但可行的解决方法。假设您位于软件包的顶层。

import sys
from os.path import dirname, basename

if __package__ is None:
    sys.path.append('..')
    __package__ = basename(dirname(sys.argv[0]))

from . import your_module

与这里的另一个答案相比,优点是您不需要更改由IDE自动生成的导入。

答案 10 :(得分:-1)

@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,并且可以找到相对的导入

答案 11 :(得分:-2)

以下示例:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py
  • 在脚本顶部添加以下行。

    # For relative imports to work
    import sys
    
  • 现在根据您要从何处导入模块,添加以下行并修改每次导入前的点。在我们的示例中,我们从 moduleA 导入 moduleX

    sys.path.append("..")
    import moduleA
    

OP 提出这个问题已经 8 年了,这些年过去了,Python 还没有在他们的更新中解决这个问题。

相对导入不起作用,因为在运行脚本时,它认为它的名称是 __main__ 而不是 filename.py