赞
踩
经常会遇到报错ImportError: attempted relative import with no known parent package
。
需要搞明白是怎么回事。
主目录就叫main吧
main/
----main.py
----pkg1/
--------a1.py
--------a2.py
--------a3.py
----pkg2/
--------b1.py
a1.py中
from .a2 import usefunc
def qqfunc():
usefunc()
a3.py中
from a1 import qqfunc
qqfunc()
main.py中
from pkg1.a1 import qqfunc
qqfunc()
可以在main下执行main.py
但是不能在pkg1目录下执行a3.py
因为此时cwd在pkg1,不能解析.a2文件。
相对引用要求cwd在相对引用的文件的上级目录
(上多少级就无所谓了)。
相反的我们把a1略作修改
a1.py中
from a2 import usefunc
def qqfunc():
usefunc()
去掉了.
此时可以在pkg1目录下执行a3.py
但不能在main下执行main.py
因为去掉.
之后采用的是module名
直接检索,
只能在cwd=pkg1时才能搜到a2这个文件。
cwd=main时搜不到。
然后是重名的情况。
重名时候优先满足cwd下的,因为cwd是sys.path[0]
。
怎么才能自己写一个package。比如文件夹是/home/aa/bb/cc/dd.py。
/home下有一个t.py。
在t.py里面import aa。
就可以直接像访问property一样,调用 aa.bb.cc.dd.somefunc()。
t.py内容
import aa
aa.bb.cc.dd.somefunc()
dd.py内容
def somefunc():
print('hello')
aa/__init__.py
添加
import bb
报错ModuleNotFoundError: No module named 'bb'
改为添加
import .bb
报错ModuleNotFoundError: No module named 'bb'
改为
from . import *
报错ModuleNotFoundError: No module named 'bb'
改为
from . import bb
t.py中成功找到了aa.bb,但是报错module 'aa.bb' has no attribute 'cc'
由是可知
from 相对位置 import package_name
的方式。因为相对位置只能写在from和import中间。from . import *
只会检索当前目录下的module,而不会导入package。同样地在/aa/bb/__init__.py
里添加
from . import cc
t.py中成功找到了aa.bb.cc,但是报错
AttributeError: module 'aa.bb.cc' has no attribute 'dd'
同样地在/aa/bb/cc/__init__.py
里添加
import .dd
报错SyntaxError: invalid syntax
改成
import dd
报错ModuleNotFoundError: No module named 'dd'
改成
from . import dd
t.py成功执行。
总之一句话,相对引用,无论import后面接package还是module,都得用
from … import的格式。
如果在/aa/__init__.py
里面这样写
from .bb.cc.dd import somefunc
在t.py里面
import aa
aa.somefunc()
aa.bb.cc.dd.somefunc()
依然可以执行成功
但是显然,子目录每多一个函数,都需要在aa/init.py里面加,也太累了。
假设/aa/hh.py下
def somefunc():
print('nono')
在/aa/__init__.py
下
from .bb.cc.dd import somefunc
from .hh import somefunc
t.py下
import aa
aa.somefunc()
aa.bb.cc.dd.somefunc()
会输出
nonono
hello
调换init里面的调用顺序,两行都是hello。
可以看出,同一个__init__.py
中的重名情况,会按照引用顺序,
后引用的覆盖前者。
PEP-0328中详细描述了相对引用的原理
https://peps.python.org/pep-0328/
Relative imports use a module’s
__name__
attribute to determine that module’s position in the package hierarchy. If the module’s name does not contain any package information (e.g. it is set to__main__
) then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.
相对引用采用属性 __name__
实现。
这就是为什么,如果你在一个 a.py 中 from .b import aa
再python a.py
会报错
ImportError: Impoe attempted relative import with no known parent package
。
因为点符号.
总是被翻译为parent(__name__)
。
即根据 __name__
属性的值,找到它的parent package。
显然这种情况下, __name__= '__main__'
。
寻找parent('__main__')
,不可能找到 '__main__'
的父包。
因此报错 no known parent package
。
但是在高一级的地方导入的话,
a的__name__ = 'packagename.a'
。
from .b import aa
就会被翻译成 from parent(packagename.a).b import aa
能找到父包 packagename
。
因此可以正常使用相对导入。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。