【问题标题】:Python imports with __init__.py使用 __init__.py 导入 Python
【发布时间】:2015-08-04 12:47:34
【问题描述】:

无论我如何在代码文件和__init__.py 文件中构建导入,我似乎都无法正确执行程序使用pytest 运行测试.当我的项目结构如下时,我需要如何编写导入:

src/
    __init__.py
    VocableFileWriter.py
    WordListHelper.py
    WordListReader.py
    XLDAttributeValueAdder.py

    exceptions/
        __init__.py
        XMLInvalidException.py
        XMLParseException.py

    gui/
        __init__.py
        GTKSignal.py
        XLDAttributeValueAdderWindow.py

    test/
        __init__.py
        test_XLDAttributeValueAdder.py

    xmlparser/
        __init__.py
        XMLParser.py

目前我有这样的:

__init__.py 文件中,我有这样的导入 (src/__init__.py):

from src import *
from src.exceptions import *
from src.xmlparser import *

在一个子包中 (src/xmlparser/__init__.py):

from src.xmlparser import *

所以我猜这些是模块的绝对项目路径。 在代码文件本身中,我像这样导入:

import os
import sys
from VocableFileWriter import VocableFileWriter
from XLDAttributeValueAdder import XLDAttributeValueAdder

但是,当我从src 上面的目录中执行代码时,使用:

./src/main.py

它告诉我:

Traceback (most recent call last):
File "./src/main.py", line 6, in <module>
    from VocableFileWriter import VocableFileWriter
File "/home/xiaolong/Development/PycharmProjects/xld-attribute-value-adder/src/VocableFileWriter.py", line 2, in <module>
    from XMLInvalidException import XMLInvalidException
ImportError: No module named 'XMLInvalidException'

它曾经是一个 PyCharm 项目,但是当我不使用 PyCharm 而是从终端运行它时,我无法使用导入结构 PyCharm 来运行它,所以我决定将整个导入内容放入我的自己的手。所以不要因为它位于 PyCharm 目录中而感到困惑。

我还希望能够使用以下示例执行测试:

py.test src/test/test_XLDAttributeValueAdder.py

我该如何解决这个烂摊子?

编辑#1: 我让程序运行了一次,但随后测试抱怨缺少模块,可能是因为它在另一个子目录中并且我尝试了很多配置,以至于我不知道我是如何让程序运行的。如果可能的话,请添加一些解释为什么某个结构是正确的并且对测试和程序本身都有效。

编辑#2: 我现在已经设法让测试运行,但现在程序不再运行了。 我清空了所有 __init__.py 文件,并在代码文件中只使用了项目绝对路径,如下所示 (src/test/test_XLDAttributeValueAdder.py):

from src.VocableFileWriter import VocableFileWriter
from src.WordListHelper import WordListHelper
from src.WordListReader import WordListReader
from src.XLDAttributeValueAdder import XLDAttributeValueAdder
from src.exceptions.XMLInvalidException import XMLInvalidException
from src.exceptions.XMLParseException import XMLParserException
from src.xmlparser.XMLParser import XMLParser
from src.test.path_helper import go_up
from src.test.path_helper import go_in

main.py:

from src.VocableFileWriter import VocableFileWriter
from src.XLDAttributeValueAdder import XLDAttributeValueAdder

./src/main.py的输出:

./src/main.py 
Traceback (most recent call last):
File "./src/main.py", line 6, in <module>
    from src.VocableFileWriter import VocableFileWriter
ImportError: No module named 'src'

编辑#3: 我试过这样的相对导入方式:

  • main.py 上移了一个目录

结构现在看起来像这样:

main.py

src/
    __init__.py
    VocableFileWriter.py
    WordListHelper.py
    WordListReader.py
    XLDAttributeValueAdder.py

    exceptions/
        __init__.py
        XMLInvalidException.py
        XMLParseException.py

    gui/
        __init__.py
        GTKSignal.py
        XLDAttributeValueAdderWindow.py

    test/
        __init__.py
        test_XLDAttributeValueAdder.py

    xmlparser/
        __init__.py
        XMLParser.py
  • __init__.py 文件为空
  • main.py 文件变得相对

导入看起来像这样:

import os
import sys
from .src.VocableFileWriter import VocableFileWriter
from .src.XLDAttributeValueAdder import XLDAttributeValueAdder

当我尝试运行main.py 文件时:

Traceback (most recent call last):
File "main.py", line 6, in <module>
    from .src.VocableFileWriter import VocableFileWriter
SystemError: Parent module '' not loaded, cannot perform relative import

但是,与main.py 文件在同一目录中有一个__init__.py 文件,也是空的。

【问题讨论】:

  • main.py 文件不应使用相对导入,当您打算直接调用它时。您将它移到包结构之外,因此它是一个独立的模块,适合对包使用绝对导入。我会澄清我的答案。

标签: python terminal python-import directory-structure pytest


【解决方案1】:

你最好使用相对导入。

src/__init__.py 中执行 from src import * 时,您只会导入在调用导入之前定义的任何内容。很可能这没什么。如果您已经定义了 __all__ 变量,您会从中获得子模块,但如果尚未定义变量,您可能还会获得 AttributeError

所以改为在需要它们的地方明确导入你需要的模块

from .VocableFileWriter import VocableFileWriter
from .exceptions.XMLInvalidException import XMLInvalidException

或者让我们在 src/gui/GTKSignal.py

中说
from ..exceptions.XMLInvalidException import XMLInvalidException

您也可以使用 Edit#2 中提到的项目绝对路径。


此外,您在调用./src/main.py 时遇到了路径问题。在这种情况下,包含src 的目录不在路径中。相反,./src/ 在路径中。因此,python 不再将其视为一个包,并且由于没有其他具有该名称的包可用,因此导入失败。

而是将您的main.py 模块放在与src 包相同的目录中,或者将其作为带有python -m src.main 的模块调用。这样做您还可以将其重命名为 src/ma​​in.py 并使用 python -m src 调用包 instad。


当使用第一种方法时,main 应该使用绝对导入结构。只需将其视为您放置在计算机上任何位置的模块,而您的 src 包位于其他地方,Python 可以找到它,就像系统安装的任何其他模块或包一样。当它位于包本身之外时并没有什么不同,因为当前目录位于sys.path

main.py

import os
import sys
from src.VocableFileWriter import VocableFileWriter
from src.XLDAttributeValueAdder import XLDAttributeValueAdder

【讨论】:

  • 项目绝对的东西并不总是有效。例如对于测试它不起作用。我猜这是因为它们在另一个目录中,或者执行 pytest 的目录与我在执行程序时所在的项目的顶层不同。我将测试相对导入。
  • 当 main.py 不在 src 文件夹之外时,我尝试使用相对导入,但这也会导致错误。将 main.py 文件放在 src 目录之外似乎很奇怪,因为它是我程序的一部分,实际上是一个源代码文件。如果可能的话,我希望它留在 src 文件夹中。这是我得到的错误,使用相对导入,main.py 文件位于 src 目录中:Traceback (most recent call last): File "./src/main.py", line 6, in &lt;module&gt; from .VocableFileWriter import VocableFileWriter SystemError: Parent module '' not loaded, cannot perform relative import
  • @Zelphir 然后你必须用python -m src.main 调用它。如果你用./src/main.py 调用它,它会被视为一个独立的模块,而不是你的 src 包的子模块。
  • 现在我得到另一个错误:Error while finding spec for 'src.main.py' (&lt;class 'AttributeError'&gt;: 'module' object has no attribute '__path__') 这太疯狂了,为什么让程序运行 pytest 测试这么复杂。在另一个项目中,我使用了 python unittest 框架,它工作得很好,因为发现或其他东西的工作方式不同。由于导入问题,pytest 似乎与正在运行的程序不兼容。我也应该在 __path__ 变量中设置什么值。这样做似乎做了一些奇怪的不必要的魔法。
  • @Zelphir 您的完整设置似乎有点不对劲。如果你想在你的包中有一个 main,考虑使用__main__.py。如果你想要一个可调用的脚本main.py,不要把它放在你的包里。此外stackoverflow.com/questions/27169750/… 可能会给你一个新错误的答案。
猜你喜欢
  • 2013-04-21
  • 1970-01-01
  • 2019-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多