一、魔术方法---反射
- 概述:运行时,区别于编译时,指的是程序被加载到内存中执行的时候
- 反射,reflection,指的是运行时获取类型信息,一个对象能够在运行时,像照镜子一样,反射出器类型信息
- 简单说,在python中,能够通过一个对象,找出其type,class,attribute或者method的能力,称为反射或者自省
- 具有反射能力的函数有:type(),isinstance(),callable(),dir(),getattr()
1、反射相关的函数和方法
需求:有一个Point类,查看它实例的属性,并修改它,动态为实例增加属性 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 print(p.__dict__) p.z = 10 print(p.__dict__) print(dir(p)) print(p.__dir__())
2、内建函数
- getattr(object,name[,default]): 通过name返回object的属性值,当属性不存在,将使用default返回;如果没有default,则抛出AttributeError,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) p1 = Point(4,5) p2 = Point(10,10) print(repr(p1),repr(p2),sep=' ') print(p1.__dict__) setattr(p1,'y',16) print(p1.__dict__) setattr(p1,'z',10) print(p1.__dict__) print(1111111111111,getattr(p1,'__dict__')) 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) p1 = Point(4,5) p2 = Point(10,10) # print(repr(p1),repr(p2),sep=' ') # print(p1.__dict__) # setattr(p1,'y',16) # print(p1.__dict__) # setattr(p1,'z',10) # print(p1.__dict__) # print(1111111111111,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) print(p1.add) print(p1.add(p2)) # 绑定向量相加 #为实例增加方法,未绑定 if not hasattr(p1,'sub'): setattr(p1,'sub',lambda self, other: Point(self.x - other.x, self.y - other.y)) print(p1.sub(p1,p1)) print('================',p1.sub) #add在谁里面,sub在谁里面(add是类的方法,sub是实例的方法) print(p1.__dict__) # print(Point.__dict__) 思考:这种动态增加属性的方式是运行时改变类或者实例的方式,但是装饰器或Mixin都是定义时就决定了 因此反射能力具有更大的灵活性
二、反射相关的魔术方法
- __getattr__(), __setattr__(), __delattr__()这三个魔术方法,分别测试
1、__getattr__()方法举例
class Base: n = 0 class Point(Base): z = 6 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) #一个类的属性会按照继承关系找,如果找不到,就会执行__getattr__()方法 #如果没有这个方法,就会抛出AttributeError异常表示找不到属性 #instance.dict-->instance.class.dict-->继承的祖先类(直到object)的dict——》调用getattr()
2、__setattr__()方法举例
class Base: n = 0 class Point(Base): z = 6 def __init__(self,x,y): print(1111111111111111111) self.x = x self.y = y def show(self): print(self.x,self.y) def __getattr__(self,item): print(2222222222222222222) return "missing {}".format(item) def __setattr__(self,key,value): print(3333333333333333333) print("setattr {}={}".format(key,value)) self.__dict__[key] = value #__setattr__()方法,可以拦截对实例属性的增加,修改操作,如果要设置生效,需要自己操作实例的__dict__ p1 = Point(4,5) print(p1.x) print(p1.z) print(p1.n) #print(p1.t) p1.x = 50 print(p1.__dict__) print(Point.__dict__) 实例通过点设置属性,如果self.x = x,就会调用__setattr__()方法,属性要加到实例的__dict__中,需要自己完成 __setattr__()方法,可以拦截对实例属性的增加,修改操作,如果要设置生效,需要自己操作实例的__dict__
3、__delattr__()
- 可以阻止通过实例删除属性的操作,但是通过类依然可以删除属性
class Base: n = 0 class Point(Base): z = 6 def __init__(self,x,y): self.x =x self.y = y def __delattr__(self,item): print('Can not del {}'.format(item)) p = Point(14,5) del p.x p.z = 15 del p.z # 实例不能删除属性 del p.z print(111111111111111,Point.__dict__) print(p.__dict__) del Point.z #类可以删除属性 print(Point.__dict__) __getattribute__ : 实例所有的属性调用都从这个方法开始 方法中为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法
4、属性查找顺序
- 实例调用__getattribute__()——> instance.__dict__--> instance.__class__.__dict__-->继承的祖先类的dict-->都没找到最后调用__getattr__()