反射 reflection 也有人称之为自省
作用:
运行时获取、添加对象的类型定义信息,包括类
内建方法:
getattr(object, name[, default]) 返回object对象的name属性的值(name必须为字符串),当属性不存在时,将使用default返回,如果没有default,就会抛出AttributeError异常。
setattr(object, name, value) 设置object对象的name属性值,如果存在则覆盖,不存在就新增。
hasattr(object, name) 判断ojbect对象是否有name属性
魔术方法:
__getattr__ 当通过搜索实例、实例的类及祖先类查找不到指定属性时调用此方法
__setattr__ 通过.访问实例属性,进行增加、修改时调用此方法
__delattr__ 当通过实例来删除属性时调用次方法
__getattribute__ 实例所有的属性调用第一个都会调用该方法
三种动态增加属性的方式:
编译期:
装饰器/Mixin
运行期:
反射
# 回顾下命令分发器 def dispatcher(): cmds = {} def reg(cmd,fn): if isinstance(cmd,str): cmds[cmd] = fn else: print('error') def run(): print(cmds.items()) while True: cmd = input('>>>').strip() if cmd == 'quit': return cmds.get(cmd,defaultfn)() def defaultfn(): print('default') return reg,run reg,run = dispatcher() reg('cmd1', lambda : print(1)) reg('cmd2', lambda : print(2)) print(run())
# 将命令分发器改装成类 class dispatcher: def cmd1(self): return 'cmd1' # def reg(self): # pass def run(self): while True: cmd = input('>>>').strip() if cmd == 'quit': return print(getattr(self,cmd,self.default)()) def default(self): return 'default' dis = dispatcher() print(dis.run())
# 使用反射为改造命令分发器 class dispatcher: def cmd1(self): return 'cmd1' def reg(self,cmd,fn): if isinstance(cmd,str): # setattr(self,cmd.strip(),fn) #报TypeError异常,反射不会自动为实例自动绑定self参数 setattr(self.__class__, cmd.strip(), fn) #为类添加属性,正常,观察运行结果__dict__的值 else: return 'Error' def run(self): print(dispatcher.__dict__) print(self.__dict__) while True: cmd = input('>>>').strip() if cmd == 'quit': return print(getattr(self,cmd,self.default)()) def default(self): return 'default' dis = dispatcher() dis.reg('cmd2',lambda self: 'cmd2') dis.reg('cmd3',lambda self: 'cmd3') print(dis.run())
一个类的属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttributeError异常表示找不到该属性。
查找属性顺序为:
instance.dict --> instance.class.dict --> 继承的祖先类(直到object类)的dict --> 调用__getattr__()
__getattr__() 魔术方法举例:
例一:
# 类装饰器本质上就是调用__call__方法 # class A: def __init__(self,x): self.x = x def __getattr__(self, item): return '__getitem__ {}'.format(item) a = A(10) print(a.x) ---运行结果-- 10
例二:
# 找不到指定属性时就会调用__getattr__方法 class A: def __init__(self,x): self.x = x def __getattr__(self, item): return "missing: {}".format(item) print(A(10).y) ---运行结果-- missing: y
例三:
# 继承情况下同样是找不到指定属性就调用__getattr__方法 class Base: n = 17 class A(Base): m = 19 def __init__(self,x): self.x = x def __getattr__(self, item): return "missing: {}".format(item) print(A(10).y) print(A(10).n) print(A(10).m) ------ missing: y 17 19
__setattr__() 魔术方法举例:
可以拦截对实例属性的增加、修改操作,如果要设置生效,需要自己操作实例的__dict__。
# 实例化时和,和实例化后,为属性赋值时就会调用__setattr__方法 class Base: n = 17 class A(Base): m = 19 def __init__(self,x): self.x = x def __getattr__(self, item): return "__getattr__: {}".format(item) # return self.item def __setattr__(self, key, value): # return "{},{}".format(key,value) # print('__setattr__:',key,value) self.__dict__[key] = value return self a = A(10) print(1,a.y) print(2,a.n) print(3,a.m) a.y = 100 print(a.__dict__) print(4,a.y) ------运行结果--------- 1 __getattr__: y 2 17 3 19 {'y': 100, 'x': 10} 4 100
__delattr__() 魔术方法举例:
# 删除实例属性时调用__delattr__方法 class Base: n = 17 class A(Base): m = 19 def __init__(self,x): self.x = x def __getattr__(self, item): print(self.__dict__) return "__getattr__: {}".format(item) # print("__getattr__: {}".format(item)) # return self.__dict__[item] def __setattr__(self, key, value): # return "{},{}".format(key,value) # print('__setattr__:',key,value) self.__dict__[key] = value # print(self.__dict__) return self def __delattr__(self, item): print('delattr:{}'.format(item)) del self.__dict__[item] #删除的是实例的属性,不是类的属性 # del self.__class__.__dict__[item] #测试是可以删除类的属性 return self a = A(10) print(1,a.y) print(2,a.n) print(3,a.m) a.y = 100 print(a.__dict__) print(4,a.y) del a.y #只能删除实例属性,不能删除类属性 # del a.m #无法删除类的属性,否则抛异常:TypeError: 'mappingproxy' object does not support item deletion print(a.__dict__) print(A.__dict__) ---运行结果---- {'x': 10} 1 __getattr__: y 2 17 3 19 {'y': 100, 'x': 10} 4 100 delattr:y {'x': 10} {'__doc__': None, '__module__': '__main__', '__delattr__': <function A.__delattr__ at 0x00000152B9112F28>, '__getattr__': <function A.__getattr__ at 0x00000152B9112E18>, 'm': 19, '__init__': <function A.__init__ at 0x00000152B9112B70>, '__setattr__': <function A.__setattr__ at 0x00000152B9112EA0>}
# 类的属性字典是一个mappingproxy对象 # 参考:https://stackoverflow.com/questions/32720492/why-is-a-class-dict-a-mappingproxy In [1]: class A: ...: n = 5 ...: In [2]: a = A() In [3]: a.n Out[3]: 5 In [5]: a.__dict__ Out[5]: {} In [6]: A.__dict__ Out[6]: mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>, '__doc__': None, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, 'n': 5}) In [7]: print(A.__dict__) {'n': 5, '__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None} In [11]: type(A.__dict__) Out[11]: mappingproxy In [12]: type(type(A.__dict__)) Out[12]: type
实例的所有属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法返回(计算后)的值或者抛出一个AttributeError异常。
它的return值将作为属性查找的结果。如果抛出AttributeError异常,则会调用__getattr__方法,因为表示属性没有找到。
__getattribute__ 方法举例:
拦截在字典查找之前
# 访问一个属性时就会调用__getattribute__方法,找不到就返回AttributeError异常 # 如果抛出了AttributeError异常,就会调用 __getattr__ 方法 class Base: n = 17 class A(Base): m = 19 def __init__(self,x): self.x = x def __getattribute__(self, item): print('__getattribute__:',item) raise AttributeError def __getattr__(self, item): return "__getattr__: {}".format(item) def __setattr__(self, key, value): print('__setattr__:',key,value) def __delattr__(self, item): print('delattr:{}'.format(item)) if hasattr(self,item): print('{} has attribute {}'.format(self,item)) del self.__dict__[item] #删除的是实例的属性,不是类的属性 return self a = A(10) print(a.y) -----运行结果------- __setattr__: x 10 __getattribute__: y __getattr__: y