论python的多级引用机制

一、多级引用在不同目录层级的调用权限
主目录就叫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,不能解析.a1文件。
相对引用要求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 中的重名情况,会按照引用顺序,
后引用的覆盖前者。