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

  

  

原文地址:https://www.cnblogs.com/i-honey/p/7861887.html