Python动态导入与反射机制实战:__import__与自省函数详解
本文详解Python中动态导入模块与反射机制的核心实现。通过__import__函数结合getattr实现模块与函数的动态加载,解决硬编码导入问题。深入解析getattr、hasattr、setattr和delattr四个自省函数的用法与内存行为,并提供基于URL的路由模拟示例,帮助开发者掌握Python高级动态特性。
一、前言
动态导入模块指的是根据运行时的实际需求,在代码执行过程中动态加载所需的模块,而非像传统方式那样在文件头部固定导入所有依赖。
反射机制(Reflection)是指利用字符串形式的名称,在模块或对象内部动态地执行查找、获取、删除或添加成员的操作。
接下来通过具体实例进行讲解。首先创建一个示例文件example.py,在其中定义几个基础的加减乘除函数,供后续演示使用。
flag = 1 # 此变量在介绍反射机制时会用到
def my_sum(a, b):
return a + b
def my_sub(a, b):
return a - b
二、动态导入模块
通常情况下,若需从其他文件引用上述函数,代码写法如下:
import example as count
# 加法
sum = count.my_sum(2, 3)
# 减法
sub = count.my_sub(6, 2)
print("sum: {}, sub: {}".format(sum, sub))
然而,若存在动态输入模块名的需求,希望随时访问导入模块中的方法或变量,应如何实现?请参考以下代码。
imp = input("请输入你需要导入的模块名称:")
count = __import__(imp) # 这种方式就是通过输入字符串导入你想导入的模块
# 加法
sum = count.my_sum(2, 3)
# 减法
sub = count.my_sub(6, 2)
print("sum: {}, sub: {}".format(sum, sub))
上述代码实现了通过动态输入模块名来导入并执行其中函数的功能。但存在一个局限:调用的函数是固定的。能否进一步改进,支持动态输入函数名并执行?请看下面的示例。
imp = input("请输入你需要导入的模块名称:")
count = __import__(imp)
func = input("请输入你需要使用的函数名:")
f = getattr(count, func, None)
# 加法
sum = f(2, 3)
print(sum)
getattr()函数的作用是:从已导入的模块中查找指定的函数func,并返回该函数的引用;若未找到,则返回None。
至此,我们实现了动态导入模块,并支持动态输入函数名以执行相应方法的功能。
不过,上述示例仍存在一个小问题:模块可能并不存放在当前目录,而是位于子目录中,结构如下图所示:

针对这种情况,应如何处理?请看下面的解决方案。
imp = input("请输入你想导入的模块名称:")
count = __import__('first_level.{}'.format(imp), fromlist=True)
fun = input("请输入你想要使用的函数名:")
f = getattr(count, fun, None)
# 加法
sum = f(2, 3)
print(sum)
三、反射机制(又称Python自省)
首先介绍Python中的四个内置自省函数:
1. getattr()
该函数是Python自省的核心,具体用法前文已述。它不仅能用于在模块中查找和获取方法或变量,同样适用于在对象中查找和获取属性和方法,此处不再赘述。
2、hasattr(object, name)
用于判断模块(或对象object)是否包含名为name的方法或变量。其实现原理是调用getattr(object, name)并检查是否抛出异常。
imp = input("请输入你想导入的模块名称:")
count = __import__('first_level.{}'.format(imp), fromlist=True)
print(hasattr(count, "my_sum")) # 判断模块count中是否存在my_sum方法,存在返回True
3、setattr(object, name, value)
这是getattr()的对应操作。参数包括一个对象、一个字符串和一个任意值。字符串name可以是对象中现有的属性或新属性,该函数将值value赋给属性name。使用示例setattr(x, y, v)等价于x.y = v。
imp = input("请输入你想导入的模块名称:")
count = __import__('first_level.{}'.format(imp), fromlist=True)
setattr(count, "flag", 0) # 即使example模块中没有flag变量,此处也成立,没有的话相当于给模块中新增一个变量flag
print(count.flag) # 打印出flag的值为0
4、delattr(object, name)
这是一组与setattr()相关的函数。参数由一个对象(Python中一切皆对象)和一个字符串name组成,name必须是对象的属性名之一。该函数用于删除由字符串name指定的对象属性。delattr(x, y)等价于del x.y。
imp = input("请输入你想导入的模块名称:")
count = __import__('first_level.{}'.format(imp), fromlist=True)
delattr(count, "flag")
print(count.flag) # 此处再打印flag的值将会报错,因为上一步已经将flag属性删除了
需要注意的是,getattr、hasattr、setattr和delattr对模块的修改仅发生在内存中,不会改变磁盘文件中实际的内容。
5、基于反射机制模拟Web框架路由的示例
需求:输入URL如www.xxx.com/example/my_sum,返回执行my_sum函数的结果。
# 动态导入模块,并执行其中函数
url = input("url: ")
target_module = url.split('/')[-2] # 分割url,取出模块名
module = __import__('first_level.' + target_module, fromlist=True)
inp = url.split("/")[-1] # 分割url,并取出url最后一个字符串
if hasattr(module, inp): # 判断在commons模块中是否存在inp这个字符串
target_func = getattr(module, inp) # 获取inp的引用
sum_ = target_func(2, 3) # 执行
print(sum_)
else:
print("404")
更多关于Python动态导入模块和反射机制的内容,请查看下面的相关文章
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













