即使使用__init__.py,也如何解决“尝试以非软件包方式进行相对导入”

本文翻译自:How to fix “Attempted relative import in non-package” even with __init__.py

I'm trying to follow PEP 328 , with the following directory structure: 我正在尝试使用以下目录结构来遵循PEP 328

pkg/
  __init__.py
  components/
    core.py
    __init__.py
  tests/
    core_test.py
    __init__.py

In core_test.py I have the following import statement core_test.py我有以下导入语句

from ..components.core import GameLoopEvents

However, when I run, I get the following error: 但是,当我运行时,出现以下错误:

tests$ python core_test.py 
Traceback (most recent call last):
  File "core_test.py", line 3, in <module>
    from ..components.core import GameLoopEvents
ValueError: Attempted relative import in non-package

Searching around I found " relative path not working even with __init__.py " and " Import a module from a relative path " but they didn't help. 到处搜索时,我发现“ 即使使用__init__.py,相对路径也不起作用 ”和“ 从相对路径导入模块 ”,但它们没有帮助。

Is there anything I'm missing here? 我在这里想念什么吗?


#1楼

参考:https://stackoom.com/question/mPEq/即使使用-init-py-也如何解决-尝试以非软件包方式进行相对导入


#2楼

Yes. 是。 You're not using it as a package. 您没有将其用作包装。

python -m pkg.tests.core_test

#3楼

To elaborate on Ignacio Vazquez-Abrams's answer: 详细阐述伊格纳西奥·巴斯克斯(Ignacio Vazquez-Abrams)的答案:

The Python import mechanism works relative to the __name__ of the current file. Python导入机制相对于当前文件的__name__ When you execute a file directly, it doesn't have its usual name, but has "__main__" as its name instead. 当您直接执行文件时,它没有通常的名称,而是使用"__main__"作为其名称。 So relative imports don't work. 因此,相对进口无效。

You can, as Igancio suggested, execute it using the -m option. 您可以按照Igancio的建议使用-m选项执行它。 If you have a part of your package that is meant to be run as a script, you can also use the __package__ attribute to tell that file what name it's supposed to have in the package hierarchy. 如果包的一部分要作为脚本运行,则还可以使用__package__属性告诉该文件在包层次结构中应具有的名称。

See http://www.python.org/dev/peps/pep-0366/ for details. 有关详细信息,请参见http://www.python.org/dev/peps/pep-0366/


#4楼

You can use import components.core directly if you append the current directory to sys.path : 如果将当前目录追加到sys.path则可以直接使用import components.core

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))

#5楼

It depends on how you want to launch your script. 这取决于您要如何启动脚本。

If you want to launch your UnitTest from the command line in a classic way, that is: 如果要以经典方式从命令行启动UnitTest ,那就是:

python tests/core_test.py

Then, since in this case 'components' and 'tests' are siblings folders, you can import the relative module either using the insert or the append method of the sys.path module. 然后,由于在这种情况下'components''tests'是同级文件夹,因此您可以使用sys.path模块的insertappend方法导入相关模块。 Something like: 就像是:

import sys
from os import path
sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
from components.core import GameLoopEvents

Otherwise, you can launch your script with the '-m' argument (note that in this case, we are talking about a package, and thus you must not give the '.py' extension), that is: 否则,您可以使用'-m'参数启动脚本 (请注意,在这种情况下,我们正在谈论的是软件包,因此您不能使用'.py'扩展名),即:

python -m pkg.tests.core_test

In such a case, you can simply use the relative import as you were doing: 在这种情况下,您可以像以前一样简单地使用相对导入:

from ..components.core import GameLoopEvents

You can finally mix the two approaches, so that your script will work no matter how it is called. 最后,您可以混合使用这两种方法,因此无论脚本如何调用,脚本都将起作用。 For example: 例如:

if __name__ == '__main__':
    if __package__ is None:
        import sys
        from os import path
        sys.path.append( path.dirname( path.dirname( path.abspath(__file__) ) ) )
        from components.core import GameLoopEvents
    else:
        from ..components.core import GameLoopEvents

#6楼

If your use case is for running tests, and it seams that it is, then you can do the following. 如果您的用例是用于运行测试的,并且可以接缝,那么您可以执行以下操作。 Instead of running your test script as python core_test.py use a testing framework such as pytest . 不用像python core_test.py那样运行测试脚本, python core_test.py使用诸如pytest类的测试框架。 Then on the command line you can enter 然后在命令行上您可以输入

$$ py.test

That will run the tests in your directory. 这将在您的目录中运行测试。 This gets around the issue of __name__ being __main__ that was pointed out by @BrenBarn. 这可以解决__name__指出的__name____main__的问题。 Next, put an empty __init__.py file into your test directory, this will make the test directory part of your package. 接下来,将一个空的__init__.py文件放入您的测试目录中,这将使测试目录成为您程序包的一部分。 Then you will be able to do 那你就可以做

from ..components.core import GameLoopEvents

However, if you run your test script as a main program then things will fail once again. 但是,如果您将测试脚本作为主程序运行,那么事情将再次失败。 So just use the test runner. 因此,只需使用测试运行器。 Maybe this also works with other test runners such as nosetests but i haven't checked it. 也许这也适用于其他测试跑步者,例如nosetests测试,但我还没有检查。 Hope this helps. 希望这可以帮助。

发布了0 篇原创文章 · 获赞 51 · 访问量 34万+

猜你喜欢

转载自blog.csdn.net/CHCH998/article/details/105470293
今日推荐