zoukankan      html  css  js  c++  java
  • Python 面向对象(四) 反射及其魔术方法

    反射 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
    

      

      

  • 相关阅读:
    java泛型的一些知识点:Java泛型--泛型应用--泛型接口、泛型方法、泛型数组、泛型嵌套
    Java遍历Map的四种方式
    Less20、21、22【报错注入+Cookie字段注入+Base64编码】
    Less18、19【报错注入+User-Agent、Referer字段注入】
    Less17【报错注入+Update注入/时间注入】
    Less-16【盲注+时间注入】
    Less-15【盲注+时间注入】
    Less-14【报错注入】
    Less-12、13【报错注入】
    Less-11【报错注入】
  • 原文地址:https://www.cnblogs.com/i-honey/p/7861887.html
Copyright © 2011-2022 走看看