Python知识分享网 - 专业的Python学习网站 学Python,上Python222
Python 引用问题 - ImportError: attempted relative import with no known parent package
发布于:2023-07-10 13:12:51

问题描述

近日在尝试引用其他文件的代码时,遇到了错误: ImportError: attempted relative import with no known parent package.
问题大致是这样的:我想在 code2.py 中引用 code1.py 的函数,如 from ..folder1.code1 import xxx,运行 code2.py 时出现错误。

 

root
├── folder1
│   └── code1.py
├── folder2
│   └── code2.py
└── main.py

 

太长不看版

如果你要在 code2.py 中引用 code1.py 的函数,那么可以:

改变文件结构,考虑在 main.py 中调用,运行 main.py

code2.py 中增加 root 的位置到搜索路径 sys.path.append, 代码使用 from folder1.code1 import xxx

用 -m 选项运行: python -m root.folder2.code2,代码可以使用 from folder1.code1 import xxx 或 from ..folder1.code1 import xxx [我认为这是最优解!]

详细解释

如果对导入的概念不是很理解的话,可能会遇到:

ModuleNotFoundError: No module named 'xxx'

ImportError: attempted relative import with no known parent package

首先明确两种导入方法:

  1. from xxx import yyy 则是从已知的模块导入
  2. “relative import” 即 from .xxx import yyy,根据从当前文件的相对路径导入。

第一种方法

具体可参考官方文档 the-module-search-path

仅适用于模块(文件夹)或脚本(文件)存在于搜索路径中,导入时,Python 解释器会首先搜索内置模块,如果没有,则去以下三个位置搜索:

  1. 当前文件所在目录
  2. 环境变量 PYTHONPATH 指定的目录
  3. Python 默认的安装目录

可以查看 sys.path,显然,当前运行脚本所在的文件夹被放在了搜索路径的首位,因此该文件夹下的所有内容均可被引入。

 

import sys
print(sys.path)
# ['/.../path-to-this-folder', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/home/thor/.local/lib/python3.10/site-packages', '/usr/local/lib/python3.10/dist-packages', '/usr/lib/python3/dist-packages']

 

要解决开头提出的问题,即引入其他文件夹下的内容,可以把 root 的位置添加到搜索路径中:(好吧,这样很不优雅……)

 

import sys
sys.path.join("/path/to/root") # 用绝对路径,需要从根目录开始
sys.path.join("..") # 用相对路径,但是命令行当前位置不能出错
 
from folder1.code1 import xxx

 

可以参考这段代码:

 

if __package__:
    from .. import config
else:
    sys.path.append(os.dirname(__file__) + '/..')
    import config

 

第二种方法

具体可参考官方文档 packages

需要明确的是,这种方法只适用于 package 内部!
当你把 code2.py 作为脚本运行时,即 python code2.py,此时 python 并不会认为它属于某一个 package, 即使存在 __init__.py。可以 print(__package__) 进行验证,作为脚本运行时为 None,否则则应该为 xxx.yyy 的形式。
(网络上有很多地方都说添加 __init__.py 就可以解决问题,但事实是并不会 ,在我的测试中,在本文提到的所有的解决方法中,添加 __init__.py 与否似乎不会带来什么影响。)

因此,开头描述的问题中,要使用相对导入的形式在 code2.py 中引用 code1.py 的代码,必须使用:

 

python -m root.folder1.code1

 

这里把 root 及其内部当作一个完整的 package,而 package 内的脚本可以使用相对导入互相引用。

❗这里不带 .py 后缀。

❗不可以为 python -m folder1.code1,此时把 folder1 及其内部当作一个完整的 package, 无法引用到以外的内容,会遇到 ImportError: attempted relative import beyond top-level package

除了命令行调用时进行调整,在脚本中 import 也是一样的道理:

 

newroot
├── root
│   ├── folder1
│   │   └── code1.py
│   ├── folder2
│   │   └── code2.py
│   └── main.py
└── upper_main.py

 

在 upper_main.py 中添加 from root.folder2 import code2 并运行时,它会把 root 当作一个包,此时code2.py中的 from ..folder1.code1 import xxx 可以正常执行

在 main.py 中添加 import folder2.code2 并运行时,它会把 folder2 当作一个包,此时 code2.py 中的 from .xx import 可以正常执行,而 from ..folder1.code1 import xxx 会遇到 ImportError: attempted relative import beyond top-level package.

其他

说明:

  1. 这里仅说明我尝试成功得出的经验,不排除有其他正确做法。
  2. 我还看到过类似 code2.py 中有 from folder1 import code1 这种做法,没有测试过其适用条件,不过模块内部感觉使用相对引用比较好。
转载自:https://www.cnblogs.com/zkmjolnir/p/17535294.html