目录
反射
概述:
- 运行时,区别于编译时,指的是程序被加载到内存中执行的时候
- 反射【reflection】,指的是运行时获取类定义的信息
- 一个对象能够在运行时,像照镜子一样,反射出其类型信息
- 简单说,在Python中,能够通过一个对象,找出其type、class、attribute或method的能力,称其反射或者自省
- 具有反射能力的函数有:type()、isinstance()、callable()、dir()、getattr()
反射相关函数和方法
需求:有个Poine类,查看它实例的属性,并修改它。动态为实例增加属性
class Point: def __init__(self,x,y): self.x = x self.y = y def __str__(self): return "Point({},{})".format(self.x,self.y) def show(self): print(self.x,self.y) p = Point(4,5) print(p) # Point(4,5) print(p.__dict__) # {'x': 4, 'y': 5} p.__dict__["y"] = 16 print(p.__dict__) # {'x': 4, 'y': 16} p.z = 10 print(p.__dict__) # {'x': 4, 'y': 16, 'z': 10} print(sorted(dir(p))) # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show', 'x', 'y', 'z'] print(sorted(p.__dir__())) # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show', 'x', 'y', 'z']
上例通过属性字典【__dict__】来访问对象的属性,本质上也是利用的反射的能力,但是上面的例子中,访问的方式不优雅,Python提供了内置的函数
内建函数 意义 getattr(object,name[,default]) 通过name返回object的属性值。当属性不存在,将使用default返回,如果没有default,则抛出AttributeErrot。name必须为字符串 setattr(object,name,value) object的属性存在,则覆盖,不存在,新增 hasattr(object,name) 判断对象是否有这个名字属性,name必须为
字符串
用上面的方法来修改上例的代码class Point: def __init__(self,x,y): self.x = x self.y = y def __str__(self): return "Point({},{})".format(self.x,self.y) def show(self): print(self.x,self.y) a = Point(4,5) b = Point(10,10) print(repr(a),repr(b)) # <__main__.Point object at 0x017B9250> <__main__.Point object at 0x017B9CD0> print(a.__dict__) # {'x': 4, 'y': 5} setattr(a,"y",16) setattr(a,"z",100) print(getattr(a,"__dict__")) # {'x': 4, 'y': 16, 'z': 100} # 动态调用方法 if hasattr(a,"show"): getattr(a,"show")() # 4 16 # 动态增加方法 # 为类增加方法 if not hasattr(Point,"add"): setattr(Point,"add",lambda self,other:Point(self.x+other.x,self.y+self.y)) print(Point.add) # <function <lambda> at 0x00BE17C8> print(a.add) # <bound method <lambda> of <__main__.Point object at 0x00C191D0>> print(a.add(b)) # Point(14,32) #为实例增加方法,为绑定 if not hasattr(a,"sub"): setattr(a,"sub",lambda self,other:Point(self.x-other.x,self.y-self.y)) print(a.sub(a,a)) # Point(0,0) print(a.sub) # <function <lambda> at 0x01746468> print(a.sub()) # 会报错,不会自动把self,传入到第一个参数里面 # add在谁里面,sub在谁里面 print(a.__dict__) # {'x': 4, 'y': 16, 'z': 100, 'sub': <function <lambda> at 0x02356468>} print(Point.__dict__) # {'__module__': '__main__', '__init__': <function Point.__init__ at 0x0230C468>, '__str__': <function Point.__str__ at 0x02356150>, 'show': <function Point.show at 0x023564B0>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None, 'add': <function <lambda> at 0x0230C228>}
思考:这种增加属性的方式和装饰器一个类、Mixin方式差异?
- 这种动态增删属性的方式时运行时改变实例的方式,但是装饰器Mixin都是定义时就决定了,因此反射能力具有更大的灵活性
练习
命令分发器,通过名称找对应的函数来执行【思路:名称找对象的方法】
class Dispathcer: def cmd1(self): print("cmd1") def reg(self,cmd,fn): if isinstance(cmd,str): setattr(self.__class__,cmd,fn) else: print("error") def run(self): while True: cmd = input("请输入命令:").strip() if cmd.strip() == "quit": return getattr(self,cmd,self.defaultfn)() def defaultfn(self): print("default") a = Dispathcer() # a.run()