我是靠谱客的博主 唠叨烧鹅,最近开发中收集的这篇文章主要介绍python的多层Import机制一、多级引用在不同目录层级的调用权限二、多级引用包装成属性的形式相对引用的翻译原理,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

经常会遇到报错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'

由是可知

  1. 相对引用package需要采用from 相对位置 import package_name的方式。因为相对位置只能写在from和import中间
  2. 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机制一、多级引用在不同目录层级的调用权限二、多级引用包装成属性的形式相对引用的翻译原理所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部