Python 引用问题 - ImportError: attempted relative import with no known parent pa...
source link: https://www.cnblogs.com/zkmjolnir/p/17535294.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
近日在尝试引用其他文件的代码时,遇到了错误: 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
首先明确两种导入方法:
from xxx import yyy
则是从已知的模块导入- “relative import” 即
from .xxx import yyy
,根据从当前文件的相对路径导入。
第一种方法
具体可参考官方文档 the-module-search-path
仅适用于模块(文件夹)或脚本(文件)存在于搜索路径中,导入时,Python 解释器会首先搜索内置模块,如果没有,则去以下三个位置搜索:
- 当前文件所在目录
- 环境变量
PYTHONPATH
指定的目录 - Python 默认的安装目录
可以查看 sys.path
,显然,当前运行脚本所在的文件夹被放在了搜索路径的首位,因此该文件夹下的所有内容均可被引入。
import sysprint(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 syssys.path.join("/path/to/root") # 用绝对路径,需要从根目录开始sys.path.join("..") # 用相对路径,但是命令行当前位置不能出错 from folder1.code1 import xxx
可以参考这段代码[1]:
if __package__: from .. import configelse: sys.path.append(os.dirname(__file__) + '/..') import config
第二种方法
具体可参考官方文档 packages
需要明确的是,这种方法只适用于 package 内部!
当你把 code2.py
作为脚本运行时,即 python code2.py
,此时 python 并不会认为它属于某一个 package, 即使存在 __init__.py
。可以 print(__package__)
进行验证,作为脚本运行时为 None
,否则则应该为 xxx.yyy
的形式[1:1]。
(网络上有很多地方都说添加 __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.
- 这里仅说明我尝试成功得出的经验,不排除有其他正确做法。
- 我还看到过类似
code2.py
中有from folder1 import code1
这种做法,没有测试过其适用条件,不过模块内部感觉使用相对引用比较好。
其他参考资料:
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK