【问题标题】:Python ModuleNotFoundError, importing between modules in projectPython ModuleNotFoundError,在项目中的模块之间导入
【发布时间】:2020-11-30 02:52:41
【问题描述】:

这一定很简单,但我发誓我已经搜索和试验了 2 天,没有出现任何错误。我设法实现的最好的结果是 IDE 指示没有错误,但在运行时失败。或表示错误,但运行成功。我在 Windows 10 上。Python 3.8.3 conda。我有 VS Code、Spyder 和 PyCharm——我已经多次卸载并重新安装了所有这些,希望其中一个能给我有用的默认行为。

我认为我有一个可重现性极低的异常,它有望用于识别问题,我将在下面解释。作为前言,我会说我希望我能找到一个完整而直接的指南来识别、声明、修复等包和模块。我看到的每个来源都有不同的建议集,似乎。我在 PYTHONPATH、sys.path、.env 工作区文件、.vscode json 文件之间迷路了。

c:\myproject
    __init__.py
    file1.py
    \myenums
        __init__.py
        category.py
        functions.py
        general.py

这样就省去了很多文件和文件夹。而且我不会详细列出我遇到的各种错误。希望这对例子能说明问题。

我在 \myproject\file1.py 中有一个函数,在 \myproject\myenums\functions.py 中有另一个函数。这些 .py 文件中的每一个都具有对其他 .py 文件的导入引用。几乎所有这些导入语句在 VS Code 中都带有红色下划线。但取决于我如何限定导入语句——我去的文件夹层次结构有多高——一个或另一个将运行,但不能同时运行。即(减少和简化实际代码的指示):

\myproject\file1.py
    import myenums.category  # red underline for error
    import myenums.functions # red underline for error
    import myenums.general   # red underline for error
    import myenums.results   # red underline for error

    print(myenums.category)  # works
    print(myenums.functions) # works
    print(myenums.general)   # works
    print(myenums.results)   # works

\myproject\myenums\functions.py
    from myenums.category import A, B, C  # red underline, note mutual parent folder
    from myenums.general import X, Y, Z   # red underline, note mutual parent folder
         
    def testfunc(value):
        if value is A.x:
            return X.a
        elif value is A.y:
            return Y.a

    print(testfunc(A.y))    # doesn't work. ModuleNotFoundError: No module named 'myenums'.

我通过在任一文件中运行而不调试来获得工作/不工作结果。

\myproject\file1.py
    import myenums.category  # red underline for error
    import myenums.functions # red underline for error
    import myenums.general   # red underline for error
    import myenums.results   # red underline for error

    print(myenums.category)  # doesn't work, ModuleNotFoundError: No module named 'category' (on import functions)
    print(myenums.functions) 
    print(myenums.general)   
    print(myenums.results)   

\myproject\myenums\functions.py
    from category import A, B, C  # red underline, note parent folder qualification removed
    from general import X, Y, Z   # red underline, note parent folder qualification removed
         
    def testfunc(value):
        if value is A.x:
            return X.a
        elif value is A.y:
            return Y.a

    print(testfunc(A.y))    # Now works!

因此,根据我是否在functions.py 中使用包含文件夹名称的资格过高,我可以让functions.py 或file1.py 运行——但不能同时运行。而且我永远不能让导入时的错误指示消失。

我完全不知道如何诊断这个问题。我是否有太多或错误的 init.py 文件?它们可能是坏文件吗?它们在记事本中看起来是空的。而且不是.py.bin。当我在 sys.path 中运行 print(p) 时,我确实看到了c:\myproject——但它是一个小写的“c”,所有的 Anaconda 引用都有大写的“C”。嗯。很久以来,我一直在尝试编写一些代码时感到迷失和无助。我不会想到在单个文件夹中的文件之间创建引用会是一个如此大的挑战。我不知道该尝试什么。

编辑:最初我认为我的问题是特定于层次结构中彼此相邻的子文件夹之间的引用,我想我会尝试通过重新排列代码以仅引用直接子文件夹来修复它 - 但这个异常似乎拍摄即便如此。另外,我最初发帖说我的异常消失了。现在我认为它仍然存在,我只是有一个错字。

【问题讨论】:

    标签: python import module package importerror


    【解决方案1】:

    这是我的建议。为测试目的组织您的文件结构:

    myproject\
        tests.py
        myproject\
            __init__.py
            file1.py
            myenums\
                __init__.py
                category.py
                functions.py
                general.py
    

    我所做的是创建一个用于测试目的的最顶层文件夹,该文件夹不包含 __init__.py 文件,即它不是主模块 myproject 的一部分。您可以从这里进行所有测试。

    一般的经验法则是,在创建 Python 包时,不应直接从包内运行脚本。

    为了说明这一点,让我们在myproject/myproject/file1.py 中编写一个函数:

    # myproject/myproject/file1.py
    
    def file1_function():
        print("Inside file1.py!")
    

    现在,我们必须在最顶层的__init__.py 中导入它,以便我们可以从函数外部访问它。

    # myproject/myproject/__init__.py
    
    from myproject import file1
    

    较早建立,我们将从包外的tests.py 运行我们的测试。在tests.py,我们可以说:

    # myproject/tests.py
    
    import myproject
    myproject.file1.file1_function()
    

    哪些输出:

    Inside file1.py!
    

    当然,您总是可以将from myproject import file1 替换为from myproject.file1 import file1_function,这样我们就可以更轻松地调用file1_function

    # myproject/tests.py
    
    import myproject
    myproject.file1_function()
    

    这给了我们与以前相同的输出。

    当我们需要导入文件/函数/类/等时会发生什么。在包装的其他地方使用?好吧,我们可以使用绝对导入。

    出于演示目的,假设functions.py 中有一段重要的代码需要file1.py 使用。

    # myproject/myproject/myenums/functions.py
    
    def really_important_function():
        print("Inside myproject/myenums/functions.py!")
    

    现在我们必须做和上面一样的事情。唯一的区别是我们试图从包内部访问它。让我们进入位于myenums__init__.py

    # myproject/myproject/myenums/__init__.py
    
    from myproject.myenums.functions import really_important_function
    

    这里我们使用绝对导入,它是源自包根的导入(myproject 是根)。

    要在file1.py 中使用它,我们必须将其导入file1.py

    # myproject/file1.py
    
    from myenums import really_important_function
    
    def use_important_function():
        really_important_function()
    

    现在我们可以从myproject/file1.py 中的myproject/myenums/functions.py 访问really_important_function,我们可以将它导入tests.py,就像我们在上面的示例中所做的那样:

    # myproject/tests.py
    
    import myproject
    
    myproject.file1.use_important_function()
    

    哪些输出:

    Inside myproject/myenums/functions.py!
    

    我认为值得注意的是,您不一定需要向上遍历包,更新每个__init__.py 只是为了测试一个无论如何都不会直接导入的函数。如果你想从tests.py 测试really_important_function,你可以这样做:

    # myproject/tests.py
    
    import myproject
    
    myproject.myenums.functions.really_important_function()
    

    这给出了正确的输出。

    结论

    另一种看待这种情况的方法如下。假设您在functions.py 中定义了“function”,并且您想在file1.py 中定义的函数中使用它:

    myproject\
        tests.py -> import myproject
                 -> myproject.use_function()
        myproject\
            __init__.py -> from myproject.file1 import use_function
            file1.py -> from myproject.myenums import function
                     -> def use_function():
                            function()
            myenums\
                __init__.py -> from myproject.myenums.functions import function
                category.py
                functions.py -> def function():
                                    ...
                general.py
    

    【讨论】:

    • 您实际上应该能够从包myproject 中的所有文件的导入路径中删除根myprojectpart。这样可以轻松地重命名包。
    • @progmatico 你是对的。在我看来,我认为这取决于个人喜好——绝对和相对进口都有其优缺点。正如您所提到的,使用绝对导入重命名模块更加困难。使用相对导入,更改项目结构可能不那么易读和容易。最终由开发者决定!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-10
    • 2021-09-29
    • 2016-05-25
    • 2020-03-11
    • 1970-01-01
    • 2021-09-12
    相关资源
    最近更新 更多