
2.3 以编程方式导入模块
案例56 检查是否能够导入某个模块
导语
在importlib.util模块下有一个名为find_spec的函数,原型如下:

name参数指定要查找的模块的名称,如果name参数所指定的模块名称使用的是相对路径,那么package参数将作为name参数中相对路径的参考对象,即name参数所指定的模块路径是相对于package参数。
例如,A包里面存在B模块,可以通过绝对路径来查找。

还可以通过相对路径来查找。

如果能找到模块的相关信息,会返回一个ModuleSpec案例;如果找不到就返回None。因此,使用find_spec函数可以检查一个模块能否被导入(不能导入的模块,函数返回None)。
操作流程
步骤1:导入find_spec函数。

步骤2:检查一下abc模块能不能被导入。

步骤3:检查一下ju_test模块是否能导入(此模块并不存在,本例中只用来测试)。

上述代码在格式化字符串时,使用了一个“内联”的if…else表达式,其含义为:如果result变量的值为None,就使用字符串“能”,否则使用字符串“不能”。
步骤4:运行案例代码,输出内容如下:

abc是Python内置的模块,因此是存在的,可以被导入,但ju_test模块是不存在的,不能导入。
案例57 使用import_module函数导入模块
导语
import_module函数可以在运行阶段导入指定模块,调用成功后,将返回该模块的引用。一般来说,import_module函数适用于以下场景:动态生成Python代码文件后再导入当前代码上下文中。
import_module函数有两个参数:name参数指定要导入的模块名称,package参数指定一个作为参考的包名。name参数所指定的模块名称中如果使用了相对路径,则该相对路径将以package参数所指定的包名为参考对象。如果name参数不使用相对路径,则package参数可以省略。
操作流程
步骤1:新建代码文件,命名为demo.py,并在其中定义以下函数。

步骤2:再新建代码文件main.py,作为程序的顶层代码。
步骤3:在main模块中,导入importlib模块,因为import_module函数由此模块公开。

步骤4:使用import_module函数导入前面定义的demo模块。

import_module函数调用后,会返回demo模块的引用,并将引用赋值给dm变量。随后,代码通过dm变量就可以访问到demo模块的成员了。
注意:向import_module函数传递参数(name或package)时需要使用字符串类型来表示模块名称。
步骤5:测试调用happy函数。

步骤6:运行main.py文件中的代码,如果看到控制台输出“快乐编程”,就说明demo模块已成功导入。
案例58 重新载入模块
导语
默认情况下,Python在导入模块后会进行缓存(在sys.modules字典中可以找到),以供程序代码使用。如果被导入模块的内容发生了变化,缓存中的模块数据并不会实时刷新,即程序代码所访问的仍然是旧版本的模块内容。
此时可以调用一下reload函数,重新载入指定模块,sys.modules字典中缓存的模块信息会被更新,随后,程序代码就可以访问到最新版本的模块成员了。被reload函数重新载入的模块必须是先前被成功导入过的(使用import语句),否则是不能加载模块的。
另外,reload函数还可以用来“还原”模块数据。由于在Python中并没有强有力的措施来阻止外部代码修改模块成员,所以模块被导入后,模块成员极有可能被有意或无意地进行更改。例如,A模块中定义变量MAX_BUFFER,初始化为1024,可是,访问A模块的代码能够直接将MAX_BUFFER的值改为3000。这种修改有时候会破坏原有代码的逻辑,造成不必要的错误。这时候可以通过reload函数重新载入A模块,MAX_BUFFER成员的值就会被还原为1024了。
操作流程
步骤1:新建代码文件test.py,然后定义变量Number,初始化为20。

步骤2:新建代码文件main.py,导入test模块。

步骤3:还要从importlib模块中导入reload函数。

步骤4:编写一个无限循环(死循环,循环条件永远为True),每当用户输入“r”时执行代码,输出test.Number的值到屏幕上;如果用户输入的不是“r”就退出循环。

步骤5:运行main.py文件的代码,输入“r”,屏幕输出如下:

步骤6:这时候,把test.py文件中Number变量的值改为50,并保存。

步骤7:再次输入“r”执行代码,输出的Number变量的值依然是20。

这表明,test模块的代码虽然被修改了,但模块缓存没有更新。
步骤8:退出程序,再次回到main.py代码文件,加上reload函数的调用。

步骤9:再次运行main.py文件,输入“r”,输出的Number变量值为50(前面步骤中已改为50)。

步骤10:将test模块中的Number变量的值改为70。

步骤11:再次输入“r”执行代码,这一次输出的Number变量的值就会自动更新为70了。

这表明,reload函数重新加载了test模块,缓存了Number变量的最新值。