反射,reflection: 指的是运行时获取类型定义信息。 在Python中,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或者自省。具有反射能力的函数有 type()、isinstance()、callable()、dir()、getattr()等
反射相关的函数和方法 -如下,查看实例的属性,更改实例的属性动态为实例增加属性 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) #打印实例 print(p.__dict__) #查看实例属性 p.__dict__['y'] = 16 #修改实例属性 'y' print(p.__dict__) #查看修改后的实数属性 p.z = 10 #动态为实例增加属性 'z=10' print(p.__dict__) ---------------------------------------------------------------------------------------- Point(4, 5) {'x': 4, 'y': 5} {'x': 4, 'y': 16} {'x': 4, 'y': 16, 'z': 10}上例是通过属性字典__dict__来访问对象的属性,本质上也是利用的反射的能力 但是Python有内置的函数,可以进行查看和修改
以上方法可以改成如下
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) def __repr__(self): return "({},{})".format(self.x,self.y) p1 = Point(4, 5) p2 = Point(10, 10) print(repr(p1), repr(p2), sep='\n') print(p1.__dict__) setattr(p1, 'y', 16) #修改实例属性 'y=16' setattr(p1, 'z', 10) #为实例增加属性 'z=10' print(getattr(p1, '__dict__')) if hasattr(p1, 'show'): # 动态调用方法 getattr(p1, 'show')() if not hasattr(Point, '__add__'): #给类动态增加魔术方法 setattr(Point, '__add__', lambda self,other: Point(self.x + other.x, self.y + other.y)) print(Point.__add__(p1,p2)) print(p1+p2) if not hasattr(p2, 'sub'): #给实例动态增加减法功能 setattr(p2, 'sub', lambda self, other: Point(self.x - other.x, self.y - other.y)) # print(p2 - p1) # 实例没有魔术方法,所以没有"-"的形式 print(p2.sub(p2,p1)) ------------------------------------------------------------------------------ (4,5) (10,10) {'x': 4, 'y': 5} {'x': 4, 'y': 16, 'z': 10} 4 16 Point(14, 26) Point(14, 26) Point(14, 26)这种动态增删属性的方式是运行时改变类或者实例的方式,但是装饰器或Mixin都是定义时就决定了,因此反射能力具有更大的灵活性
通过名称找对应的函数执行。思路:名称找对象的方法
class Dispatcher(): def __init__(self): pass def reg(self,name,fn): return setattr(self,name,fn) #用setattr内建函数给实例增加属性 def run(self): # 注册函数 cmd = input(">>>") if cmd.strip('') == '': print('you must input cmd') return else: c, *params = cmd.strip('').replace(',', ' ').split() args = [] kwargs = {} for param in params: par = param.split('=') if len(par) == 1: args.append(int(param)) elif len(par) == 2: kwargs[par[0]] = int(par[1]) getattr(self,c,lambda *args,**kwargs :print('Unkown the cmd {}'.format(cmd)))(*args,**kwargs) #用getattr内建函数获取实例属性值(执行) def add(x, y): #被注册的函数(实例增加功能) print(x + y) return def sum(x, y): #被注册的函数(实例增加功能) print(x -y) return fx = Dispatcher() fx.reg('add',add) fx.reg('sum',sum) fx.run()上例中使用getattr方法找到对象的属性的方式,比自己维护一个字典来建立名称和函数之间的关系的方式好多了。
__getattr __()、__setattr __()、__delattr __()这三个魔术方法
__getattr __()方法
class Base: n = 'ABCD' class Point(Base): z ='abcd' def __init__(self, x, y): self.x = x self.y = y def show(self): print(self.x, self.y) def __getattr__(self, item): return "missing {}".format(item) p1 = Point(4,5) print(p1.x) print(p1.z) print(p1.n) print(p1.t) ----------------------------------------------------------------------- 4 abcd ABCD missing t 实例属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttributeError异常表示找不到属性查找属性顺序为 instance.__dict __ --> instance.__class __.__dict __ --> 继承的祖先类(直到object)的__dict __ —找不到–> 调用__getattr __()上列可以阻止通过实例来删除属性的操作。但是通过类依然可以删除属性
方法总结
魔术方法意义__getattr __()当通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法__setattr __()通过.访问实例属性,进行增加、修改都要调用它,属性要加到实例的__dict__中,就需要自己手动完成__delattr __()当通过实例来删除属性时调用此方法__getattribute __实例所有的属性调用都从这个方法开始,它的return值将作为属性查找的结果 实例查找的顺序: 实例调用__getattribute__() --> instance.__dict __ --> instance. __class __.__dict __ --> 继承的祖先类(直到object)的__dict __ --> 调用__getattr __()