问题:相对进口量为十亿次

我来过这里:

以及很多我没有复制的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会给出该错误消息,“非包装”的含义,为什么以及如何定义“包装”以及准确的答案,这些措辞足以使幼儿园的学生理解

I’ve been here:

and plenty of URLs that I did not copy, some on SO, some on other sites, back when I thought I’d have the solution quickly.

The forever-recurring question is this: With Windows 7, 32-bit Python 2.7.3, how do I solve this “Attempted relative import in non-package” message? I built an exact replica of the package on pep-0328:

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

The imports were done from the console.

I did make functions named spam and eggs in their appropriate modules. Naturally, it didn’t work. The answer is apparently in the 4th URL I listed, but it’s all alumni to me. There was this response on one of the URLs I visited:

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.

The above response looks promising, but it’s all hieroglyphs to me. So my question, how do I make Python not return to me “Attempted relative import in non-package”? has an answer that involves -m, supposedly.

Can somebody please tell me why Python gives that error message, what it means by “non-package”, why and how do you define a ‘package’, and the precise answer put in terms easy enough for a kindergartener to understand.


回答 0

脚本与模块

这是一个解释。简短的版本是直接运行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)。

Script vs. Module

Here’s an explanation. The short version is that there is a big difference between directly running a Python file, and importing that file from somewhere else. Just knowing what directory a file is in does not determine what package Python thinks it is in. That depends, additionally, on how you load the file into Python (by running or by importing).

There are two ways to load a Python file: as the top-level script, or as a module. A file is loaded as the top-level script if you execute it directly, for instance by typing python myfile.py on the command line. It is loaded as a module if you do python -m myfile, or if it is loaded when an import statement is encountered inside some other file. There can only be one top-level script at a time; the top-level script is the Python file you ran to start things off.

Naming

When a file is loaded, it is given a name (which is stored in its __name__ attribute). If it was loaded as the top-level script, its name is __main__. If it was loaded as a module, its name is the filename, preceded by the names of any packages/subpackages of which it is a part, separated by dots.

So for instance in your example:

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

if you imported moduleX (note: imported, not directly executed), its name would be package.subpackage1.moduleX. If you imported moduleA, its name would be package.moduleA. However, if you directly run moduleX from the command line, its name will instead be __main__, and if you directly run moduleA from the command line, its name will be __main__. When a module is run as the top-level script, it loses its normal name and its name is instead __main__.

Accessing a module NOT through its containing package

There is an additional wrinkle: the module’s name depends on whether it was imported “directly” from the directory it is in, or imported via a package. This only makes a difference if you run Python in a directory, and try to import a file in that same directory (or a subdirectory of it). For instance, if you start the Python interpreter in the directory package/subpackage1 and then do import moduleX, the name of moduleX will just be moduleX, and not package.subpackage1.moduleX. This is because Python adds the current directory to its search path on startup; if it finds the to-be-imported module in the current directory, it will not know that that directory is part of a package, and the package information will not become part of the module’s name.

A special case is if you run the interpreter interactively (e.g., just type python and start entering Python code on the fly). In this case the name of that interactive session is __main__.

Now here is the crucial thing for your error message: if a module’s name has no dots, it is not considered to be part of a package. It doesn’t matter where the file actually is on disk. All that matters is what its name is, and its name depends on how you loaded it.

Now look at the quote you included in your question:

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.

Relative imports…

Relative imports use the module’s name to determine where it is in a package. When you use a relative import like from .. import foo, the dots indicate to step up some number of levels in the package hierarchy. For instance, if your current module’s name is package.subpackage1.moduleX, then ..moduleA would mean package.moduleA. For a from .. import to work, the module’s name must have at least as many dots as there are in the import statement.

… are only relative in a package

However, if your module’s name is __main__, it is not considered to be in a package. 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.

Scripts can’t import relative

What you probably did is you tried to run moduleX or the like from the command line. When you did this, its name was set to __main__, which means that relative imports within it will fail, because its name does not reveal that it is in a package. Note that this will also happen if you run Python from the same directory where a module is, and then try to import that module, because, as described above, Python will find the module in the current directory “too early” without realizing it is part of a package.

Also remember that when you run the interactive interpreter, the “name” of that interactive session is always __main__. Thus you cannot do relative imports directly from an interactive session. Relative imports are only for use within module files.

Two solutions:

  1. If you really do want to run moduleX directly, but you still want it to be considered part of a package, you can do python -m package.subpackage1.moduleX. The -m tells Python to load it as a module, not as the top-level script.

  2. Or perhaps you don’t actually want to run moduleX, you just want to run some other script, say myfile.py, that uses functions inside moduleX. If that is the case, put myfile.py somewhere elsenot inside the package directory – and run it. If inside myfile.py you do things like from package.moduleA import spam, it will work fine.

Notes

  • For either of these solutions, the package directory (package in your example) must be accessible from the Python module search path (sys.path). If it is not, you will not be able to use anything in the package reliably at all.

  • Since Python 2.6, the module’s “name” for package-resolution purposes is determined not just by its __name__ attributes but also by the __package__ attribute. That’s why I’m avoiding using the explicit symbol __name__ to refer to the module’s “name”. Since Python 2.6 a module’s “name” is effectively __package__ + '.' + __name__, or just __name__ if __package__ is None.)


回答 1

这确实是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

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

这是一个通用的配方,经过修改以适合作为示例,我现在使用它来处理以程序包形式编写的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)。

Here’s a general recipe, modified to fit as an example, that I am using right now for dealing with Python libraries written as packages, that contain interdependent files, where I want to be able to test parts of them piecemeal. Let’s call this lib.foo and say that it needs access to lib.fileA for functions f1 and f2, and lib.fileB for class Class3.

I have included a few print calls to help illustrate how this works. In practice you would want to remove them (and maybe also the from __future__ import print_function line).

This particular example is too simple to show when we really need to insert an entry into sys.path. (See Lars’ answer for a case where we do need it, when we have two or more levels of package directories, and then we use os.path.dirname(os.path.dirname(__file__))—but it doesn’t really hurt here either.) It’s also safe enough to do this without the if _i in sys.path test. However, if each imported file inserts the same path—for instance, if both fileA and fileB want to import utilities from the package—this clutters up sys.path with the same path many times, so it’s nice to have the if _i not in sys.path in the boilerplate.

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)

The idea here is this (and note that these all function the same across python2.7 and python 3.x):

  1. If run as import lib or from lib import foo as a regular package import from ordinary code, __package is lib and __name__ is lib.foo. We take the first code path, importing from .fileA, etc.

  2. If run as python lib/foo.py, __package__ will be None and __name__ will be __main__.

    We take the second code path. The lib directory will already be in sys.path so there is no need to add it. We import from fileA, etc.

  3. If run within the lib directory as python foo.py, the behavior is the same as for case 2.

  4. If run within the lib directory as python -m foo, the behavior is similar to cases 2 and 3. However, the path to the lib directory is not in sys.path, so we add it before importing. The same applies if we run Python and then import foo.

    (Since . is in sys.path, we don’t really need to add the absolute version of the path here. This is where a deeper package nesting structure, where we want to do from ..otherlib.fileC import ..., makes a difference. If you’re not doing this, you can omit all the sys.path manipulation entirely.)

Notes

There is still a quirk. If you run this whole thing from outside:

$ python2 lib.foo

or:

$ python3 lib.foo

the behavior depends on the contents of lib/__init__.py. If that exists and is empty, all is well:

Package named 'lib'; __name__ is '__main__'

But if lib/__init__.py itself imports routine so that it can export routine.name directly as lib.name, you get:

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

That is, the module gets imported twice, once via the package and then again as __main__ so that it runs your main code. Python 3.6 and later warn about this:

$ 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__'

The warning is new, but the warned-about behavior is not. It is part of what some call the double import trap. (For additional details see issue 27487.) Nick Coghlan says:

This next trap exists in all current versions of Python, including 3.3, and can be summed up in the following general guideline: “Never add a package directory, or any directory inside a package, directly to the Python path”.

Note that while we violate that rule here, we do it only when the file being loaded is not being loaded as part of a package, and our modification is specifically designed to allow us to access other files in that package. (And, as I noted, we probably shouldn’t do this at all for single level packages.) If we wanted to be extra-clean, we might rewrite this as, e.g.:

    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

That is, we modify sys.path long enough to achieve our imports, then put it back the way it was (deleting one copy of _i if and only if we added one copy of _i).


回答 3

因此,在与其他许多人讨论了这一问题之后,我遇到了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的模块中使用。也许这很明显,但是我在这里提供此说明,以防其他因上述相对导入问题而感到沮丧的人可以使用它。

So after carping about this along with many others, I came across a note posted by Dorian B in this article that solved the specific problem I was having where I would develop modules and classes for use with a web service, but I also want to be able to test them as I’m coding, using the debugger facilities in PyCharm. To run tests in a self-contained class, I would include the following at the end of my class file:

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

but if I wanted to import other classes or modules in the same folder, I would then have to change all my import statements from relative notation to local references (i.e. remove the dot (.)) But after reading Dorian’s suggestion, I tried his ‘one-liner’ and it worked! I can now test in PyCharm and leave my test code in place when I use the class in another class under test, or when I use it in my web service!

# 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

The if statement checks to see if we’re running this module as main or if it’s being used in another module that’s being tested as main. Perhaps this is obvious, but I offer this note here in case anyone else frustrated by the relative import issues above can make use of it.


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

Here is one solution that I would not recommend, but might be useful in some situations where modules were simply not generated:

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

回答 5

我有一个类似的问题,我不想更改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 *

I had a similar problem where I didn’t want to change the Python module search path and needed to load a module relatively from a script (in spite of “scripts can’t import relative with all” as BrenBarn explained nicely above).

So I used the following hack. Unfortunately, it relies on the imp module that became deprecated since version 3.4 to be dropped in favour of importlib. (Is this possible with importlib, too? I don’t know.) Still, the hack works for now.

Example for accessing members of moduleX in subpackage1 from a script residing in the subpackage2 folder:

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

A cleaner approach seems to be to modify the sys.path used for loading modules as mentioned by 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 *

回答 6

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

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

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

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

__name__ changes depending on whether the code in question is run in the global namespace or as part of an imported module.

If the code is not running in the global space, __name__ will be the name of the module. If it is running in global namespace — for example, if you type it into a console, or run the module as a script using python.exe yourscriptnamehere.py then __name__ becomes "__main__".

You’ll see a lot of python code with if __name__ == '__main__' is used to test whether the code is being run from the global namespace – that allows you to have a module that doubles as a script.

Did you try to do these imports from the console?


回答 7

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

@BrenBarn’s answer says it all, but if you’re like me it might take a while to understand. Here’s my case and how @BrenBarn’s answer applies to it, perhaps it will help you.

The case

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

Using our familiar example, and add to it that moduleX.py has a relative import to ..moduleA. Given that I tried writing a test script in the subpackage1 directory that imported moduleX, but then got the dreaded error described by the OP.

Solution

Move test script to the same level as package and import package.subpackage1.moduleX

Explanation

As explained, relative imports are made relative to the current name. When my test script imports moduleX from the same directory, then module name inside moduleX is moduleX. When it encounters a relative import the interpreter can’t back up the package hierarchy because it’s already at the top

When I import moduleX from above, then name inside moduleX is package.subpackage1.moduleX and the relative import can be found


回答 8

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

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

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.

Wrote a little python package to PyPi that might help viewers of this question. The package acts as workaround if one wishes to be able to run python files containing imports containing upper level packages from within a package / project without being directly in the importing file’s directory. https://pypi.org/project/import-anywhere/


回答 9

为了使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也会知道谁在程序包的顶层

To make Python not return to me “Attempted relative import in non-package”. package/

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

This error occurs only if you are applying relative import to the parent file. For example parent file already returns main after you code “print(name)” in moduleA.py .so THIS file is already main it cannot return any parent package further on. relative imports are required in files of packages subpackage1 and subpackage2 you can use “..” to refer to the parent directory or module .But parent is if already top level package it cannot go further above that parent directory(package). Such files where you are applying relative importing to parents can only work with the application of absolute import. If you will use ABSOLUTE IMPORT IN PARENT PACKAGE NO ERROR will come as python knows who is at the top level of package even if your file is in subpackages because of the concept of PYTHON PATH which defines the top level of the project


声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。