Python + 子模块:ImportError: 尝试相对导入但未知父包

3

我一直在处理Python的相对导入问题。

我的项目结构如下:

root_dir
├── main.py
├── Pipfile
├── Pipfile.lock
├── unit_tests
│   ├── __init__.py
│   ├── test_first.py
│   └── test_second.py
└── my_package
    ├── __init__.py
    ├── first.py
    ├── second.py
    └── third.py

我想从位于my_package的文件中导入一组函数到test_first.py中。
根据官方文档,语法如下:
from ..my_package import first

当我这样做时,会出现以下异常:
Traceback (most recent call last):
  File "/home/user/dev/root_dir/my_package/unit_tests/first.py", line 8, in <module>
    from ..my_package import first
ImportError: attempted relative import with no known parent package

我也尝试使用以下语法导入文件:

from root_dir.my_package import first

当我这样做时,我会得到以下异常:

ModuleNotFoundError: No module named 'root_dir'

需要强调的是,我正在使用 pipenv 来处理虚拟环境。 你有任何想法为什么会发生这种情况吗?

谢谢。

4个回答

2

2

首先,我会给你我用过的代码。然后,我会简要解释一下。这里有一个简短的函数,应该可以让你从根目录导入。

解决方案

import os, sys

def access_root_dir(depth = 1):
    current_dir = os.path.dirname(os.path.realpath(__file__))
    parent_dir = os.path.dirname(current_dir)
    args: list = [parent_dir]
    for _ in range(depth):
        args.append('..')
    
    rel_path = os.path.join(*args)
    sys.path.append(rel_path) 

# At the top of test_first.py
access_root_dir(2)
import root_dir   

由于根目录是test_first.py的祖父目录,您需要进入深度2:

解释

我在我的项目中遇到了同样的问题,并发现通过改变sys.path来从父目录导入可以解决。像sys.path.append(abs_path_to_root)这样将根目录的绝对路径附加到其中似乎不起作用,除非所有内容都是包的一部分。默认情况下,Python不会访问导入中的父目录。

# Case 1: Relative path to parent - works
current_dir = os.path.dirname(os.path.realpath(__file__))
relative_path = os.path.join(current_dir, '..')
sys.path.append(relative_path)
import parent_module_name # This works for me (Python 3.9)

# Case 2: Absolute path to parent - Doesn't work 
current_dir = os.path.dirname(os.path.realpath(__file__))
parent_dir = os.path.dirname(current_dir)
sys.path.append(parent_dir)
import parent_module_name # raises ModuleNotFoundError

1

当前工作目录不是一个包。使用os.getcwd()查看它是什么,但很可能是root_dir

在这种情况下,您可以直接按名称导入顶级包,而无需进行相对导入;您应该直接从my_package导入,如下所示:

from my_package import first

...或者:

from my_package.first import do_something

复制的文件树:

root_dir
├── main.py
├── my_package
│   ├── __init__.py
│   └── first.py
└── unit_tests
    ├── __init__.py
    └── test_first.py

main.py的内容:

from unit_tests import test_first

if __name__ == "__main__":
    test_first.test_something()

如果您尝试进行相对导入,例如:from ..my_package import first,则会出现以下错误:

ImportError: attempted relative import beyond top-level package

0

相对导入只允许在包内部进行。一个包必须是自给自足的,只依赖于标准库或全局或虚拟环境“lib”文件夹中的第三方包。一个包不得访问其周围的任何Python模块,这意味着包内的模块不能以相对方式导入包的顶层文件夹之外的任何内容。要指定包的顶层文件夹,请在其根目录下创建__init__.py文件,并将以下代码放入其中:

# Marking the containing folder the top level folder of the package...
__package__ = ''

在您的示例中,您可以有两个包:'root_dir'和'my_package'。因此,在'root_dir'创建__init__.py,并且您已经在'my_package'中创建了__init__.py并将以下内容放入其中:
# Marking the containing folder the top level folder of the package...
__package__ = ''

现在您可以使用以下相对导入:

from ..my_package import first

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接