python的反射
反射
在程序运行时候,动态的给一个对象添加属性。区别于编译时就已经确定好的状态,反射是在程序运行时动态的获取类型定义信息。在python中反射体现为我们可以使用一个字符串去获取这个对象的属性,或者给对象动态的添一个属性,这个字符串可能是临时生成的,没人知道他会是什么,只有程序运行时候才会进行添加。这和我们在编码状态下就写死的硬编码不同。
反射函数
python提供了一些反射的函数
getattr(object, name : str, default) # 获取对象上名为name的属性,没有则为defalut
setattr(object, name : str, value) # 运行时动态的给对象添加一个name名字的属性,或者方法
hasattr(object, name : str, default) # 查询一个对象是否有这个字符串对应的属性
动态加载模块
import importlib path = "lib.request.Request" module_name, class_name = path.rsplit(".", maxsplit=1) # 通过字符串module_name,程序找到了该文件路径,并将这个静态文件加载到内存,创建了一个模块对象,该对象被赋值给module变量 module = importlib.import_module(module_name) # module是一个模块对象, 又通过class_name这个字符串,在其module模块中寻找该同名变量 plugin_module = getattr(module, class_name, EmptyPlug) # 获取到插件模块 plugin = plugin_module() # 实例化
如果在"lib.request.Request"路径下确实存在该模块和该类,那么这个模块和这个类都将被加载为一个内存的对象,并复制给对应的变量,哪个这个变量将能调用对应的属性和方法执行。这就实现了对模块的动态加载。
反射相关魔术方法
__getattr__
__setattr__
__delattr__
__getattribute__
# 调用ins.x时,在实例的mro中没有找到,即将引发AttributeError调用该函数item为"x" def __getattr__(self, item) # item 为self.访问的属性的字符串 return # getattr(class,key)函数会触发对象的 __getattribute__方法,如果方法没有找到,转而调用 # __getattr__,仍未找到使用默认值。 # ins.x 获取属性值时候调用,使用成员访问符访问这个实例的属性时候就会调用这个函数 # 除了 ins.x = value 是直接访问 __setattr__()
def __getattribute__(self, item): # 任何访问 self.属性的方法都将调用该属性,被调用的属性名,即为item的值 return value
# 调用 ins.x = value 时执行,重写该方法后,所有的self.key = value 都会调用该函数 def setattr(self, key, value): # key和value分别为"x" 和 value
# 执行del ins.x 时执行该函数 def delattr(self, key) # key 为 字符串"x"
这些方法将一个函数对属性的操作,映射到了一个函数上,并将访问的属性名的字符串,作为了函数的参数,这是一种对象和字符串之间的映射关系,也是通过反射实现。实际上python的对象大量的使用反射机制,可以查看对象的__dict__方法,对象将所有的属性都通过字符串的形式保存在该字典中,我们可以通过访问字典的方式去访问对象的上的属性。