python的魔术方法之反射

    xiaoxiao2025-01-12  18

    文章目录

    反射反射反射相关的魔术方法

    反射


    反射

    反射,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有内置的函数,可以进行查看和修改


    内建函数意义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.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 __()
    __setattr __()方法 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) def __setattr__(self, key, value): print("setattr {}={}".format(key, value)) p1 = Point(4,5) print(p1.x) # missing,why 实例通过 . 来访问,被__setattr__拦截了 print(p1.z) print(p1.n) print(p1.t) # missing p1.x = 50 print(p1.x) # missing, why? 属性x的值被重新赋值,但是并没有写到字典里 print(p1.__dict__) p1.__dict__['x'] = 60 #手动把属性"x"=60 写入字典中 print(p1.__dict__) print(p1.x) # p1.setattr('m',100) 不能这样设置 p1.__dict__['m']=100 #手动把属性"m"=100 写入字典中 print(p1.__dict__) ----------------------------------------------------------------------- setattr x=4 setattr y=5 missing x abcd ABCD missing t setattr x=50 missing x {} {'x': 60} 60 {'x': 60, 'm': 100} 实例通过 . 点号设置属性,例如self.x=x属性赋值,就会调用__setattr__(),属性要加到实例的__dict__中,就需要自己完成__setattr __()方法,可以拦截对实例属性的增加、修改操作,如果要设置生效,需要自己操作实例的__dict __
    __delattr __()方法 class Point: Z = 5 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(Point.__dict__) print(p.__dict__) del Point.Z print(Point.__dict__) ---------------------------------------------------------- Can not del x Can not del z Can not del Z {'__module__': '__main__', 'Z': 5, '__init__': <function Point.__init__ at 0x0000020A29399620>, '__delattr__': <function Point.__delattr__ at 0x0000020A29399598>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None} {'x': 14, 'y': 5, 'z': 15} {'__module__': '__main__', '__init__': <function Point.__init__ at 0x0000020A29399620>, '__delattr__': <function Point.__delattr__ at 0x0000020A29399598>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None}

    上列可以阻止通过实例来删除属性的操作。但是通过类依然可以删除属性


    __getattribute __() 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): return item p1 = Point(4,5) print(p1.__dict__) print(p1.x) print(p1.z) print(p1.n) print(p1.t) print(Point.__dict__) print(Point.z) --------------------------------------------------- __dict__ x z n t {'__module__': '__main__', 'z': 6, '__init__': <function Point.__init__ at 0x0000020A29399AE8>, '__getattr__': <function Point.__getattr__ at 0x0000020A29399268>, '__getattribute__': <function Point.__getattribute__ at 0x0000020A29399D08>, '__doc__': None} 6 实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError异常它的return值将作为属性查找的结果。如果抛出AttributeError异常,则会直接调用__getattr__方法,因为表示属性没有找到

    方法总结

    魔术方法意义__getattr __()当通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法__setattr __()通过.访问实例属性,进行增加、修改都要调用它,属性要加到实例的__dict__中,就需要自己手动完成__delattr __()当通过实例来删除属性时调用此方法__getattribute __实例所有的属性调用都从这个方法开始,它的return值将作为属性查找的结果 实例查找的顺序: 实例调用__getattribute__() --> instance.__dict __ --> instance. __class __.__dict __ --> 继承的祖先类(直到object)的__dict __ --> 调用__getattr __()
    最新回复(0)