上节简单说了 import 系统 和 __import__ 函数, 这节大概说下 importlib 中的 import_module, 和 package 相关的东西。
importlib.import_module(name, package=None)
name 是需要导入模块的名称,可以以相对或绝对方式导入;绝对方式是写上模块的绝对包路径,例如要导入 pkg 下的 module 模块,可以这样直接导入
import importlib
m = importlib.import_module('pkg.module')
返回值是模块对象 module,而不是 pkg ,这是和 __import__ 之间最大的区别,这也应该是 Python 官方文档上写的为什么不建议使用 __import__的主要原因,假如前面的包路径很长的话,如果用 __import__ 函数去导入的话只会返回最上层的那个 package 对象,那样用起来就很不方便。
至于 importlib.import_module 的相对路径方式,就必须要指定 package 参数,作为寻找名字为 name 的模块的锚点,大概是这种形式:
import importlib
#导入 pkg.mod
m = importlib.import_module('..mod', 'pkg.subpkg')
感觉这种方式应该很少用到。做个了解。
下面开始介绍 package
package 在 python 中好比文件系统中的文件和文件夹、package 负责管理模块和组织层级结构,package 也属于模块,但不是所有的模块都是 package, 可以理解为 .py 文件就是一个非 package 模块,而里面包含有一个 __init__.py 文件的文件夹即是一个 package module。 在 python 3.3 版本开始引入了新的概念,就是如果下面没有 __init__.py 文件, 这个文件夹同样是一个 package, 为了区分两者,前者称为 Regular packages, 后者称为 Namespace packages。
package 模块对象有一个 __path__ 属性,而普通的模块对象没有这个属性,这个属性是一个 存放 string 的可迭代对象,对象的值是一些路径,在导入它下面的子模块时,在这些路径下搜索子模块。
Regular packages
Regular package 的例子引用官方文档上的代码:
parent/
__init__.py
one/
__init__.py
two/
__init__.py
three/
__init__.py
当导入 parent.one 时,parent/__init__.py 和 parent/one/__init__.py 会隐式执行,并且里面定义的变量会绑定到当前 package 的命名空间中。__init__.py 中可以像普通模块一样包含任何代码。
当通过 foo.bar.baz 形式导入模块时,最后一个冒号之前的名称必然都是package, 最后一个名称可能是 package 可能是 普通模块,说明普通模块不能包含子模块,import 系统首先会导入 foo, 然后再通过 foo.__path__ 找到 bar, 再通过 bar.__path__ 导入 baz, 中间环节如果导入失败,就会抛出一个 ModuleNotFoundError 异常。
这节先说到这,下节会说明导入模块的去向以及简单了解下 NameSpace package。