运行时,区别于编译时,指的是程序被加载到内存中执行的时候 反射,reflection,指的是运行时获取类型定义信息 一个对象能够在运行时,像照镜子一样,反射处其类型信息 简单说,在python中,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或者自省 具有反射能力的函数有type()、isinstance()、callable()、dir()、getattr()等 反射相关的函数和方法 需求 有一个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) # Point(4, 5) print(p.__dict__) # {'x': 4, 'y': 5} p.__dict__['y'] = 16 # {'x': 4, 'y': 16} print(p.__dict__) # {'x': 4, 'y': 16, 'z': 10} p.z = 10 print(p.__dict__) print(dir(p)) print(p.__dir__())上例通过属性字典__dict__来访问对象的属性,本质上也是利用的反射的能力。 Python提供了内置的函数
内建函数意义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)) print(p1.__dict__) # {'x': 4, 'y': 5} setattr(p1, 'y', 16) setattr(p1, 'z', 16) print(getattr(p1, '__dict__')) # {'x': 4, 'y': 16, 'z': 16} # 动态调用方法 if hasattr(p1, 'show'): getattr(p1, 'show')() # Point(4, 16) # 动态增加方法 # 为类增加方法 if not hasattr(Point, 'add'): setattr(Point, 'add', lambda self,other: Point(self.x +other.x, self.y + other.y)) print(Point.add) # <function <lambda> at 0x000002018E6E51E0> print(p1.add) # <bound method <lambda> of <__main__.Point object at 0x000001EC6C2A86A0>> print(p1.add(p2)) # 绑定 # Point(14, 26) # 为实例增加方法,未绑定 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)) # Point(0, 0) print(p1.sub) # <function <lambda> at 0x0000018C324E5620> # add在谁里面, sub在谁里面 print(p1.__dict__) # {'x': 4, 'y': 16, 'z': 16, 'sub': <function <lambda> at 0x00000180472D5620>} print(Point.__dict__) # {'__module__': '__main__', '__init__': <function Point.__init__ at 0x00000180472D5158>,\ # '__str__': <function Point.__str__ at 0x00000180472D5268>, 'show': <function Point.show at 0x00000180472D5598>, \ # '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>,\ # '__doc__': None, 'add': <function <lambda> at 0x00000180472D51E0>}这种动态增删属性的方式是运行时改变类或者实例的方式,但是装饰器或Mixin都是定义时就决定了,因此反射能力具有更大的灵活性
思路:名称找对象的方法
class Dispatcher: def __init__(self): pass def reg(self, name, fn): setattr(self, name, fn) def run(self): while True: cmd = input('>>>').strip() if cmd == 'quit': break getattr(self, cmd, lambda :print('Unknowm Cmd {}'.format(cmd)))() dis = Dispatcher() dis.reg('ls', lambda :print('ls')) dis.run()上例中使用getattr方法找到对象的属性的方式,比自己维护一个字典来建立名词和函数之间的关系的方式好多了
__getattr__()、__setattrr__()、__delattr__()
实例属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttrbuteError异常表示找不到属性
查找属性的顺序: instance.__dict__ --> instance.__class__.__dict__ --> 继承的祖先类(直到object)的__dict --> 找不到调用__getattr__()实例通过.点号设置属性,例如self.x = x属性赋值,就会调用__setattr_-(),属性要加到实例的__dict__中,就需要自己完成
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) def __setattr__(self, key, value): print("setattr {}={}".format(key,value)) self.__dict__[key] = value # 操作字典__setattr__()方法,可以拦截对实例属性的增加、修改操作,如果要设置生效,需要自己操作实例的__dict__
__getattr__和__setattr__综合使用
class B: b = 200 class A(B): z = 100 d = {} def __init__(self, x, y): self.x = x setattr(self, 'y', y) self.__dict__['a'] = 5 def __getattr__(self, item): print('------', item) return self.d[item] def __setattr__(self, key, value): print('setattr key ', key) print('setattr key ', value) self.d[key] = value def __delattr__(self, item): print('can not del {}'.format(item)) a = A(4, 5) print(a.__dict__) print(A.__dict__) print(a.x , a.y) print(a.a) # 执行结果 setattr key x setattr key 4 setattr key y setattr key 5 {'a': 5} {'__module__': '__main__', 'z': 100, 'd': {'x': 4, 'y': 5},\ '__init__': <function A.__init__ at 0x000001062E6C5158>,\ '__getattr__': <function A.__getattr__ at 0x000001062E6C5268>, \ '__setattr__': <function A.__setattr__ at 0x000001062E6C5598>, \ '__delattr__': <function A.__delattr__ at 0x000001062E6C5620>, \ '__doc__': None} ------ x ------ y 4 5 5执行结果
Can not del x Can not del z Can not del Z {'__module__': '__main__', 'Z': 5, '__init__': <function Point.__init__ at 0x0000029FBF8B5158>, \ '__delattr__': <function Point.__delattr__ at 0x0000029FBF8B5268>, '__dict__': <attribute '__dict__' of 'Point' objects>, \ '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None} {'x': 4, 'y': 5, 'z': 15} {'__module__': '__main__', '__init__': <function Point.__init__ at 0x0000029FBF8B5158>, \ '__delattr__': <function Point.__delattr__ at 0x0000029FBF8B5268>, '__dict__': <attribute '__dict__' of 'Point' objects>,\ '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None}可以阻止通过实例来删除属性的操作。但是通过类依然可以删除属性
实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError异常
它的return值将作为属性查找的结果如果抛出AttributeError异常,则会直接调用__getattr__方法,表示属性没有找到 class Base: n = 0 class Point(Base): z = 6 def __init__(self, x , y): self.x = x self.y = y def __getattr__(self, item): return "missing {}".format(item) def __getattribute__(self, item): # raise AttributeError("Not Found") # pass # return self.__dict__[item] # RecursionError return object.__getattribute__(self, item) p1 = Point(4, 5) print(p1.__dict__) # {'x': 4, 'y': 5} print(p1.x) # 4 print(p1.z) # 6 print(p1.n) # 0 print(p1.t) # missing t print(Point.__dict__) # {'__module__': '__main__', 'z': 6, '__init__': <function Point.__init__ at 0x0000029AF7765158>, \ '__getattr__': <function Point.__getattr__ at 0x0000029AF7765268>, \ '__getattribute__': <function Point.__getattribute__ at 0x0000029AF7765598>, '__doc__': None} print(Point.z) # 6 __getattribute__方法中为了避免在该方法中无限的递归,它的实现应该永远调用基类的同名方法打以访问需要的任何属性,例如object.__getattribute__(self, item)除非明确地知道__getattribute__方法用来做什么,否则不要使用它