什么是反射
反射是一个很重要的概念,它可以把字符串映射到实例的变量或者实例的方法然后可以去执行调用、修改等操作。它有四个重要的方法:
- getattr 获取指定字符串名称的对象属性
- setattr 为对象设置一个对象
- hasattr 判断对象是否有对应的对象(字符串)
- delattr 删除指定属性
attr是属性英文的前几个字母,属性指的是类中类变量、实例变量和方法。但是要注意不能是私有的,如果你的变量是以“_”开头,那将无法获取。
反射常常用在动态加载模块的场景中。
#!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.com class TestObj(object): def __init__(self, name, age): self.name = name self.age = age def displayName(self): print("displayName方法执行,打印姓名:", self.name) def AAA(self): print("I am AAA.") def main(): to = TestObj("Tom", 23) # 查看 to 实例里面是否有 name 这个属性 if hasattr(to, "name"): print("实例 to 中有 name 属性。") print(getattr(to, "name")) else: print("实例 to 中没有 name 属性。") if hasattr(to, "displayName"): print("实例 to 中有 displayName 属性。") getattr(to, "displayName")() else: print("实例 to 中没有 displayName 属性。") if hasattr(to, "AAA"): print("实例 to 中有 AAA 属性。") getattr(to, "AAA")() else: print("实例 to 中没有 AAA 属性,将会设置。") setattr(to, "AAA", AAA) # 参数:实例、方法名称、具体方法 相当于 to.AAA = AAA 第一个AAA是函数在实例中的名称, 第二个AAA是把哪个函数放进去,两者只是恰好这里名称一样 # to.AAA(to) # 这里一定要主动传递一个实例进去,因为它不会自动装配self getattr(to, "AAA")(to) if __name__ == '__main__': main()
AAA是动态装载到实例里面去的。
可能有些人还没明白反射,反射就是把字符串反射成内存对象,看下面的例子
#!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.com class TestObj(object): def __init__(self, name): self.name = name def displayname(self): print(self.name) def main(): to = TestObj(name="Tom") cmd = input("输入:") if hasattr(to, cmd): pass else: setattr(to, cmd, displayname) func = getattr(to, cmd) func(to) if __name__ == '__main__': main()
根据用户输入来调用函数(这个函数肯定要提前的真实存在)。我这里无论我输入什么都可以执行上面的displayname方法。现在应该明白字符串映射到方法了吧。
反射使用
通过字符串导入模块
#!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.com temp = "re" model = __import__(temp) def main(): txt = "hj123uo" pattern = model.compile(r"[0-9]+") print(model.search(pattern, txt).group()) if __name__ == '__main__': main()
以字符串的形式使用模块的方法
#!/usr/bin/env python # -*- coding: utf-8 -*- # Author: rex.cheny # E-mail: rex.cheny@outlook.com temp = "re" # 要引入的模块 func = "compile" # 要使用的方法 model = __import__(temp) # 导入模块 function = getattr(model, func) # 找到模块中的属性 def main(): txt = "hj123uo" pattern = function(r"[0-9]+") # 这里执行funcation()就等于执行re.compile()函数 print(model.search(pattern, txt).group()) if __name__ == '__main__': main()
反射到底有什么用?
上面使用re.compile()函数的整个过程看起来很麻烦,但是你要知道这就等于实现了动态加载和执行所需要的模块或方法而不需要全部写入到PY文件中,当然具体需要执行的方法你也要提前实现。典型的使用场景就是web的URL路由。目前所有的web框架的URL路由基本都是这个原理。
用户输入不同的URL如何加载不同的PY文件以及调用里面的方法呢?你想一想Django里面,它并不是这样的,它依然需要你设置URL以及该URL对应的PY文件,为什么?因为这样调试方便,当然你能力足够也可以给它改写成反射的机制。