您的位置:首页 > 编程语言 > Python开发

解决python相对导入出现错误:Attempted relative import beyond toplevel package

2017-12-21 13:58 525 查看
相对导入的官方解释(中文):http://python3-cookbook.readthedocs.io/zh_CN/latest/c10/p03_import_submodules_by_relative_names.html

相对导入解决的问题就是消除绝对路径带来的硬编码问题,具体请看文档。

但是在使用相对导入的时候会出来各种错误,其中最让人费解的可能就是:Attempted relative import beyond toplevel package

其实这个原因是因为包的层次引起的,跟包的路径查找有很大的关系。

先来聊聊python的包的查找方式:

简单的来说,Python 有个PYTHONPATH环境变量和安装路径,这个环境变量和安装路径决定了sys.path(是一个list类型)的值,而python查找包时,python会查找当前路径,然后会按顺序查找sys.path中的路径。

import sys
print sys.path
通过上面代码,你可以看到默认的查找路径。

在这里需要注意的是,python2中,是按上述方式进行的(先查找当前路径,再到sys.path),但是在python3中,是先查找sys.path,找不到再查找当前路径的。

具体可以看官方文档(中文):http://www.pythondoc.com/pythontutorial3/modules.html

如果你看了上面的这边官方文档,你一定看到了python找打印包路径的方法:

print __name__

现在让我们通过这命令来解释,在使用相对导入的时候,为什么会出现:Attempted relative import beyond toplevel package

先来看一下测试目录结构:

.
├── run.py
└── test
├── dir1
│   ├── __init__.py
│   └── moudle1
│   ├── a.py
│   └── __init__.py
├── dir2
│   ├── __init__.py
│   ├── __init__.pyc
│   └── moudle2
│   ├── b.py
│   ├── b.pyc
│   ├── __init__.py
│   └── __init__.pyc
├── run.py
└── test.py
a.py中有一个函数:

def test():
pass
现在,要实现的是,在 b. py 中引用 a.py 的内容:

print __name__

from ...dir1.moudle1.a import test
注意到,有两个 run.py 和 test/run.py
这两个文件的代码都很简单,只有一行代码:
from test.dir2.moudle2.b import test然后分别运行上面的run脚本:
python test/run.py 这个命令将会打印 b 的包路径并且报错
包路径:dir2.moudle2.b
Traceback (most recent call last):
File "test/run.py", line 1, in <module>
from dir2.moudle2.b import test
File "~/Code/python/mytest/test/dir2/moudle2/b.py", line 3, in <module>
from  ...dir1.moudle1.a import test
ValueError: Attempted relative import beyond toplevel package


从打印的包路径来看,这个包的完整路径只是到dir2就没了。而我们的相对导入在解析相对路径的时候,是根据所在脚本的包路径来解析的。
仔细分析一下这个路径:

dir2moudle2b
..(父目录).(当前目录)包名
从这个表就可以看出来,三级目录 ... (三个点) 已经超出了最顶层的目录结构。因此才会报错。
再看下一个命令:

python run.py 这个命令打印的 b 的包路径是:
包路径:test.dir2.moudle2.b


分析下来就是:

testdir2moudle2b
...(三级目录)..(父目录).(当前目录)当前目录
通过上面的比较,可以知道,包路径跟运行脚本所在的目录有关系,而更加本质的原因是,跟包的查找路径有关系(python会在运行脚本所在的路径查找包)。对此有疑惑的同学,也可以试试,比如:把不同的包路径添加到 sys.path 中,然后在其他地方执行run脚本,是可以得到同样的结果的。

最后,总结一下,在使用相对导入的时候一定要注意包路径和包的查找路径。要在最顶层的目录添加到 sys.path 中,或者 在最顶层运行脚本。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐