例如 "from .. import m0718"或"from . import m0718_temp"会提示在"父包未知时尝试相关导入"
这是因为:python导包和导模块路径为sys.path和脚本所在目录,在python3.8中是先在脚本所在目录查找,查找不到时再从左至右在sys.path的目录列表中查找,打印sys.path的第一个''就表示脚本所在目录。形如"$ python3.8 m07182.py"这样的调用,Python解释器在cd的pwd目录中执行m07182.py模块,此时m07182.py是一个模块而不是一个包,没有包路径的概念,用户理解的上层目录".."或当前目录".",并不能被python解释器理解为"包的相对路径"。
解决方法:1、不使用".."或"."的相对路径表示,将要导入的模块或包的路径加到sys.path或放到sys.path的任何一个目录下;
2、形如"$ python3.8 -m ~/Files/pack/m07184.py"执行脚本文件,显式地带父目录或祖目录来执行一个模块,使python解释器获悉包路径,然后使用相对路径就可以被python解释器解析出绝对路径予以导入
3、具体原理以后再学习,这里先给出拙见
目录结构为
/home/haypin/Files/m0718.py 被调脚本
/home/haypin/Files/pack/m0718_temp.py 被调脚本
/home/haypin/Files/pack/m07182.py 主调脚本
/home/haypin/Files/pack/m07184.py 主调脚本
/home/haypin/Files/pack/subpack/m07185.py 被调脚本
可以看到,第一种方式cd 到主调脚本m07182.py所在目录~/Files/pack,print(__file__)只打印相对pwd目录下的文件名,print(__package__)则打印None,说明只把m07182.py当做模块而不是包,不是包就没有路径,不能用".."或"."表示父目录或当前目录,因为没法解析。
第二种方式cd到主调脚本和被调脚本共同的父目录或祖目录,然后以包的形式调用主调脚本模块,此时print(__file__)会打印出主调脚本的绝对路径,并不是相对于pwd的相对路径,print(__package__)打印"Files.pack"的包路径,"pack"是主调脚本模块的当前目录".","Files"是主调脚本模块的父目录"..",此时python解释器能将".."与"."按照包路径解析成对应的父目录与"当前目录",从而可以执行相对路径导包。
如果是通过包导入的模块中有相对路径导入,则在导包时也要显式提供父包".."与当前包"."
通过祖包"..."的相对路径导入也一样:
同理,cd到祖目录并以包的方式执行主调模块,从而使python解释器获悉"祖目录.父目录.当前目录"的包路径,就可以使用相对路径导入祖目录下的模块。
子包的传递导入:
单独导入包(package):单独import某个包名称时,不会导入该包中所包含的所有子模块。
c.py导入同级目录B包的子包B1包的b2模块,执行b2模块的print_b2()方法:
c.py代码
import B
B.B1.b2.print_b2()
运行c.py,会报错。
解决办法:
B/__init__.py代码
from . import B1#其中的.表示当前目录
B/B1/__init__.py代码
from . import b2
此时,执行c.py,成功打印:b2。
20200725补充:
学习Django源码时看到了builtin的/usr/lib/python3.8/importlib/__init__.py中定义的导包函数import_module(name, package=None):
def import_module(name, package=None):
"""Import a module.
The 'package' argument is required when performing a relative import. It
specifies the package to use as the anchor point from which to resolve the
relative import to an absolute import.
"""
'''导入一个模块,当执行相对导入时参数'package'会被用到,它指定了被用来作为锚点的包,
该锚点用来分解相对导入为绝对导入。也就是说相对导入仍然需要包名来确定一个相对
sys.path的绝对路径来进行导入,本质上也是绝对路径导入。
'''
level = 0
if name.startswith('.'):
if not package:
msg = ("the 'package' argument is required to perform a relative "
"import for {!r}")
raise TypeError(msg.format(name))
for character in name:
if character != '.':
break
level += 1
return _bootstrap._gcd_import(name[level:], package, level)
importlib.import_module(name, package)的使用,这里包名package与模块名name都是字符串,并且模块名要自己带个'.',其实就相当于"import package.name",没这个点还不行
'''a temp module m0809'''
from sys import path
path.append('/home/haypin/Files')
import importlib
m08092 = importlib.import_module('.m08092','pack.subpack')
print(m08092)
python3.8执行输出:
execute pack/__init__.py successed!
execute pack/subpack/__init__.py successed!
<module 'pack.subpack.m08092' from '/home/haypin/Files/pack/subpack/m08092.py'>
haypin@ubt:~/Files$