例如 "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       被调脚本

python同项目相对路径 python相对路径导入_python同项目相对路径

python同项目相对路径 python相对路径导入_python_02

可以看到,第一种方式cd 到主调脚本m07182.py所在目录~/Files/pack,print(__file__)只打印相对pwd目录下的文件名,print(__package__)则打印None,说明只把m07182.py当做模块而不是包,不是包就没有路径,不能用".."或"."表示父目录或当前目录,因为没法解析。

第二种方式cd到主调脚本和被调脚本共同的父目录或祖目录,然后以包的形式调用主调脚本模块,此时print(__file__)会打印出主调脚本的绝对路径,并不是相对于pwd的相对路径,print(__package__)打印"Files.pack"的包路径,"pack"是主调脚本模块的当前目录".","Files"是主调脚本模块的父目录"..",此时python解释器能将".."与"."按照包路径解析成对应的父目录与"当前目录",从而可以执行相对路径导包。

如果是通过包导入的模块中有相对路径导入,则在导包时也要显式提供父包".."与当前包"."

python同项目相对路径 python相对路径导入_相对路径_03

通过祖包"..."的相对路径导入也一样:

python同项目相对路径 python相对路径导入_当前目录_04

python同项目相对路径 python相对路径导入_python同项目相对路径_05

同理,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$