• chapter9.4、魔术方法反射


    反射

    概述

      运行时,区别于编译时,指的是程序被加载到内存中执行时

      反射,reflection,指的是运行时获取类型定义信息

      大部分动态语言提供了反射

      一个对象运行时,像照镜子一样,反射出类型信息

      自省也是反射的一种称呼

    在Python中能够通过一个对象,找出其 type,class ,attrbute或method的能力,称为反射或者自省。

    有反射能力的函数有:

    type()  返回类,相当于.__class__

    isinstance(对象,类型)  返回bool值,判断类型,类型可以是多个类型包起来的元组,判断其中是否有对象的类型

    callable()  看一个对象是否为可调用对象,调__call__方法

    dir()  返回类的或者对象的所有成员的列表,调用__dir__方法

    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(sorted(dir(p)))
    print(sorted(p.__dir__()))

    属性字典__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.x,self.y)
    
    p1 = Point(4,5)
    p2 = Point(10,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)
    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)

    getattr(object, name[, default=None])  通过name返回object的属性值。当属性不存在,使用default返回,如果没有default就抛出AttributeError,name要求必须是字符串。

    setattr(object,name,value)  object属性存在,则覆盖,不存在,新增

    hasattr(object,name)    返回bool,判断对象是否有这个名字的属性,name必须为字符串

    delattr 删除

    可以绑定在对象和类上

    可以对任何对象直接增加属性。使用内建函数或者 对象.属性访问

    装饰器和Mixin都是定义时就修改了类的属性,而setattr能在运行时修改属性,或者删除属性。

    反射有更大的灵活性

    命令分发器:通过名称找对应函数执行

    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('command not found'))()
    
    dis=Dispatcher()
    dis.reg('ls',lambda :print('ls function'))
    dis.run()

    使用getattr方法找对象的属性的方式,比自己维护一个字典来建立名称和函数之间的关系的方式好多了

    反射的相关魔术方法

    __getattr__   实例查找属性的顺序,会按照自己的字典,类的字典,继承的祖先类的字典,直到object的字典,找不到就找__getattr__的方法,没有就抛AttributeError

    getattr是查找属性没找到后的最后一道防线,然后就是抛异常

    __setattr__  实例通过.点号设置,例如self.x= x ,就会调用__setattr__,属性要加到实例的__dict__中,就需要自己完成。该方法可以拦截实例属性的增加,修改操作,如果要设置生效,就需要自己操作实例的__dict__。

    class Point:
        s = 100
        d = {}
    
        def __init__(self,x,y):
            self.x = x##调setattr
            self.y = y
            self.__dict__['a'] = 5##不调setattr
    
        def __getattr__(self, item):
            return self.d[item]
    
        def __setattr__(self, key, value):
            print(key)
            print(value)
            self.d[key] = value##将属性存放到类的属性中,此处可以改成任何存储位置
    
        def __delattr__(self, item):
            print('cannot del {}'.format(item))
    
    
    p1 = Point(3,2)##调用setattr,添加属性
    print(p1.__dict__)##字典里有a的属性
    print(Point.__dict__)
    print(p1.x,p1.y)##调用getattr
    print(p1.a)##在实例的字典找到了,不去往后找了

    __delattr__  可以阻止通过实例删除属性的操作,但通过类依然可以删除属性。

    class Point:
        z = 100
    
        def __init__(self,x,y):
            self.x = x
            self.y = y
    
        def __delattr__(self, item):
            print('cannot del {}'.format(item))
    
    p1 = Point(3,2)
    del p1.x
    p1.z = 15
    del p1.z
    print(Point.__dict__)
    print(p1.__dict__)
    del Point.z     ##通过类删除属性,类的属性删除成功
    print(Point.__dict__)

    __getattribute__  实例的所有属性访问,都会先调用__getattribute__方法,它阻止了属性的查找,它将返回值,或者抛异常,AttributeError

      它的返回值就是属性查找的结果,

      如果抛出异常,就会直接调用__getattr__方法,因为表示属性没找到。

    class B:
        n =0
    
    class Point(B):
        z = 100
        def __init__(self,x,y):
            self.x = x
            self.y = y
    
        def __getattr__(self, item):
            return 'misssing {}'.format(item)
    
        def __getattribute__(self, item):
            return item
            ###给啥返回啥
    
    p1 = Point(3,2)
    print(p1.__dict__)##返回__dict__
    print(p1.x)
    print(p1.z)
    print(p1.n)
    print(p1.t)##对象访问属性,都去调__getattribute__
    print(Point.__dict__)##返回类字典,
    print(Point.z)##类访问属性

    __getattr__方法中为了避免该方法中无限递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如object.__getattr__(self,name)

    class B:
        n =0
    
    class Point(B):
        z = 100
    
        def __init__(self,x,y):
            self.x = x
            self.y = y
    
        def __getattr__(self, item):
            return 'misssing {}'.format(item)
    
        def __getattribute__(self, item):
            #raise AttributeError      ##抛出异常
            ##pass             ##只有这句返回None,返回值就是None
            ##return self.__dict__[item]        ##递归调用
            return object.__getattribute__(self,item)   ##到object找__getattribute__方法
    
    p1 = Point(3,2)
    print(p1.__dict__)##返回__dict__
    print(p1.x)
    print(p1.z)
    print(p1.n)
    print(p1.t)
    print(Point.__dict__)
    print(Point.z)

    总结:

    __getattr__()        当通过搜索实例、实例的类及祖先类查不到属性,就会调用此方法

    __setattr__()   通过访问实例的属性,进行增加修改,都会调用它

    __delattr__()   当通过实例来删除属性时调用此方法

    __getattribute__()  实例的所有属性调用都从这个方法开始

  • 相关阅读:
    java 数组的定义
    java 流程控制语句
    java 跳转语句(break、continue)
    java 循环嵌套
    java连接数据库
    用JAVA给数据库增加和修改数据代码
    简单匿名内部类的写法
    抽象类的定义以及简单代码
    继承多态的简单代码
    单例模式
  • 原文地址:https://www.cnblogs.com/rprp789/p/9690745.html
Copyright © 2020-2023  润新知