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

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

我正在尝试使用以下目录结构来遵循PEP 328

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

core_test.py我有以下进口声明

from ..components.core import GameLoopEvents

但是,当我运行时,出现以下错误:

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

到处搜索时,我发现“ 即使使用__init__.py,相对路径也无法使用 ”和“ 从相对路径导入模块 ”,但是它们没有帮助。

我在这里想念什么吗?

I’m trying to follow PEP 328, with the following directory structure:

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

In core_test.py I have the following import statement

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.

Is there anything I’m missing here?


回答 0

是。您没有将其用作包装。

python -m pkg.tests.core_test

Yes. You’re not using it as a package.

python -m pkg.tests.core_test

回答 1

详细阐述伊格纳西奥·巴斯克斯·阿布拉姆斯答案:

Python导入机制相对于__name__当前文件起作用。直接执行文件时,它没有通常的名称,但是具有"__main__"以它的名称命名。因此,相对进口无效。

您可以按照Igancio的建议使用该-m选项执行它。如果包的一部分要作为脚本运行,则还可以使用__package__属性告诉该文件在包层次结构中应具有的名称。

参见http://www.python.org/dev/peps/pep-0366/详细信息,。

To elaborate on Ignacio Vazquez-Abrams’s answer:

The Python import mechanism works relative to the __name__ of the current file. When you execute a file directly, it doesn’t have its usual name, but has "__main__" as its name instead. So relative imports don’t work.

You can, as Igancio suggested, execute it using the -m option. 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.

See http://www.python.org/dev/peps/pep-0366/ for details.


回答 2

import components.core如果将当前目录附加到,则可以直接使用sys.path

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

You can use import components.core directly if you append the current directory to sys.path:

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

回答 3

这取决于您要如何启动脚本。

如果要以经典方式从命令行启动UnitTest,那就是:

python tests/core_test.py

然后,由于在这种情况下‘components’‘tests’是同级文件夹,因此您可以使用sys.path模块的insertappend方法导入相关模块。就像是:

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

否则,您可以使用’-m’参数启动脚本(请注意,在这种情况下,我们正在谈论一个软件包,因此,您不能使用‘.py’扩展名),即:

python -m pkg.tests.core_test

在这种情况下,您可以像以前一样简单地使用相对导入:

from ..components.core import GameLoopEvents

最后,您可以将两种方法混合使用,以便您的脚本无论调用方式如何都可以正常工作。例如:

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

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:

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. 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:

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

回答 4

在core_test.py中,执行以下操作:

import sys
sys.path.append('../components')
from core import GameLoopEvents

In core_test.py, do the following:

import sys
sys.path.append('../components')
from core import GameLoopEvents

回答 5

如果您的用例是用于运行测试的,并且可以接缝,那么您可以执行以下操作。不要像python core_test.py使用那样运行测试框架来运行测试脚本pytest。然后在命令行上您可以输入

$$ py.test

这将在您的目录中运行测试。这得到周围人的问题__name__的存在__main__,是由@BrenBarn指出。接下来,将一个空__init__.py文件放入您的测试目录,这将使测试目录成为您程序包的一部分。那你就可以做

from ..components.core import GameLoopEvents

但是,如果您将测试脚本作为主程序运行,那么事情将再次失败。因此,只需使用测试运行器。也许这也适用于其他测试运行程序,例如,nosetests但我尚未检查。希望这可以帮助。

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. 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. Next, put an empty __init__.py file into your test directory, this will make the test directory part of your package. 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. Hope this helps.


回答 6

我的快速解决方案是将目录添加到路径:

import sys
sys.path.insert(0, '../components/')

My quick-fix is to add the directory to the path:

import sys
sys.path.insert(0, '../components/')

回答 7

问题在于您的测试方法,

你试过了 python core_test.py

那么您将收到此错误 ValueError:尝试在非包中进行相对导入

原因:您正在从非包装来源测试包装。

因此,请从软件包源测试模块。

如果这是您的项目结构,

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

cd pkg

python -m tests.core_test # dont use .py

或从外部pkg /

python -m pkg.tests.core_test

.如果要从同一目录中的文件夹导入,则为Single 。每退一步,再增加一个。

hi/
  hello.py
how.py

how.py

from .hi import hello

如果你想从hello.py导入

from .. import how

Issue is with your testing method,

you tried python core_test.py

then you will get this error ValueError: Attempted relative import in non-package

Reason: you are testing your packaging from non-package source.

so test your module from package source.

if this is your project structure,

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

cd pkg

python -m tests.core_test # dont use .py

or from outside pkg/

python -m pkg.tests.core_test

single . if you want to import from folder in same directory . for each step back add one more.

hi/
  hello.py
how.py

in how.py

from .hi import hello

incase if you want to import how from hello.py

from .. import how

回答 8

旧线程。我发现__all__= ['submodule', ...]__init__.py文件中添加,然后from <CURRENT_MODULE> import *在目标中使用可以正常工作。

Old thread. I found out that adding an __all__= ['submodule', ...] to the __init__.py file and then using the from <CURRENT_MODULE> import * in the target works fine.


回答 9

您可以使用from pkg.components.core import GameLoopEvents,例如我使用pycharm,下面是我的项目结构图像,我只是从根包中导入,然后就可以了:

You can use from pkg.components.core import GameLoopEvents, for example I use pycharm, the below is my project structure image, I just import from the root package, then it works:


回答 10

正如Paolo所说,我们有2种调用方法:

1) python -m tests.core_test
2) python tests/core_test.py

它们之间的区别是sys.path [0]字符串。由于解释将在导入时搜索sys.path,因此我们可以使用tests/core_test.py

if __name__ == '__main__':
    import sys
    from pathlib import Path
    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
    from components import core
    <other stuff>

之后,我们可以使用其他方法运行core_test.py:

cd tests
python core_test.py
python -m core_test
...

注意,py36仅经过测试。

As Paolo said, we have 2 invocation methods:

1) python -m tests.core_test
2) python tests/core_test.py

One difference between them is sys.path[0] string. Since the interpret will search sys.path when doing import, we can do with tests/core_test.py:

if __name__ == '__main__':
    import sys
    from pathlib import Path
    sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
    from components import core
    <other stuff>

And more after this, we can run core_test.py with other methods:

cd tests
python core_test.py
python -m core_test
...

Note, py36 tested only.


回答 11

这种方法对我有用,并且比某些解决方案更混乱:

try:
  from ..components.core import GameLoopEvents
except ValueError:
  from components.core import GameLoopEvents

父目录位于我的PYTHONPATH中,并且__init__.py父目录和此目录中都有文件。

上面的代码始终在python 2中有效,但是python 3有时会遇到ImportError或ModuleNotFoundError(后者在python 3.6中是新功能,是ImportError的子类),因此以下调整对我在python 2和3中均有效:

try:
  from ..components.core import GameLoopEvents
except ( ValueError, ImportError):
  from components.core import GameLoopEvents

This approach worked for me and is less cluttered than some solutions:

try:
  from ..components.core import GameLoopEvents
except ValueError:
  from components.core import GameLoopEvents

The parent directory is in my PYTHONPATH, and there are __init__.py files in the parent directory and this directory.

The above always worked in python 2, but python 3 sometimes hit an ImportError or ModuleNotFoundError (the latter is new in python 3.6 and a subclass of ImportError), so the following tweak works for me in both python 2 and 3:

try:
  from ..components.core import GameLoopEvents
except ( ValueError, ImportError):
  from components.core import GameLoopEvents

回答 12

尝试这个

import components
from components import *

Try this

import components
from components import *

回答 13

如果有人正在寻找解决方法,我偶然发现了一个。这里有一些背景。我想测试文件中的一种方法。当我从内部运行时

if __name__ == "__main__":

它总是抱怨相对进口。我尝试应用上述解决方案,但由于许多嵌套文件,每个文件都有多个导入,因此无法正常工作。

这就是我所做的。我刚刚创建了一个启动器,一个外部程序,它将导入必要的方法并调用它们。虽然这不是一个很好的解决方案,但它可以工作。

If someone is looking for a workaround, I stumbled upon one. Here’s a bit of context. I wanted to test out one of the methods I’ve in a file. When I run it from within

if __name__ == "__main__":

it always complained of the relative imports. I tried to apply the above solutions, but failed to work, since there were many nested files, each with multiple imports.

Here’s what I did. I just created a launcher, an external program that would import necessary methods and call them. Though, not a great solution, it works.


回答 14

这是一种会惹恼所有人但效果很好的方法。在测试中运行:

ln -s ../components components

然后只需像往常一样导入组件。

Here’s one way which will piss off everyone but work pretty well. In tests run:

ln -s ../components components

Then just import components like you normally would.


回答 15

这非常令人困惑,如果您使用的是像pycharm这样的IDE,那就更令人困惑了。对我有用的方法:1.进行pycharm项目设置(如果从VE或python目录运行python)2.定义的方式没有错。有时它与from folder1.file1导入类一起使用

如果它不起作用,请使用import folder1.file1。3.您的环境变量应在系统中正确提及或在命令行参数中提供。

This is very confusing, and if you are using IDE like pycharm, it’s little more confusing. What worked for me: 1. Make pycharm project settings (if you are running python from a VE or from python directory) 2. There is no wrong the way you defined. sometime it works with from folder1.file1 import class

if it does not work, use import folder1.file1 3. Your environment variable should be correctly mentioned in system or provide it in your command line argument.


回答 16

由于您的代码包含if __name__ == "__main__",而不会作为包导入,因此最好使用它sys.path.append()来解决问题。

Because your code contains if __name__ == "__main__", which doesn’t be imported as a package, you’d better use sys.path.append() to solve the problem.