概述
前言
经常会遇到报错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'
由是可知
- 相对引用package需要采用
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
。
因此可以正常使用相对导入。
最后
以上就是唠叨烧鹅为你收集整理的python的多层Import机制一、多级引用在不同目录层级的调用权限二、多级引用包装成属性的形式相对引用的翻译原理的全部内容,希望文章能够帮你解决python的多层Import机制一、多级引用在不同目录层级的调用权限二、多级引用包装成属性的形式相对引用的翻译原理所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复