Python +子模块:ImportError:尝试相对导入,但没有已知的父包

lrpiutwd  于 2023-05-13  发布在  Python
关注(0)|答案(4)|浏览(212)

我一直在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中。
根据official documentation,语法应该如下:

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来处理虚拟环境。知道为什么会这样吗
谢谢

nkoocmlb

nkoocmlb1#

首先,我会给予你的代码,为我工作。那我就简单解释一下。下面是一个简短的函数,它应该允许您从根目录导入。

解决方案

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
mzmfm0qo

mzmfm0qo2#

current working directory is not a package使用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
kwvwclae

kwvwclae3#

我认为问题是你的根目录不被认为是一个python模块。
为了进行相对导入,您应该将“init.py”添加到根目录。
https://docs.python.org/3/reference/import.html

jpfvwuh4

jpfvwuh44#

相对导入只允许在包内进行。软件包必须是自高效的,并且只依赖于标准库或第三方软件包,无论是在全局还是虚拟环境的“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

相关问题