反射 & 命令分发器

  

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

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

一个对象能够在运行时,像找镜子一样,反射处其类型信息

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

具有反射能力的函数: type()      isinstance()        callable()        dir()            getattr()

def dispatcher():
    cmds={}
    def reg(cmd,fn):
        if isinstance(cmd,(str,)):
            cmds.setdefault(cmd,fn)
        else:
            raise TypeError('TypeError')
        print(cmds)

    def run():
        while True:
            cmd=input('cmd: ').strip()
            if cmd.lower() == 'q':
                return
            print(cmds.get(cmd,default)())

    def default():
        print('default')
    return reg,run

reg,run=dispatcher()
reg('b1',lambda :print(11111111))
reg('b2',lambda :222)
print(run())
class Dispatcher:
    def cmd1(self):
        return 'cmd1'
    def reg(self,cmd,fn):
        if isinstance(cmd,(str,)):
            # setattr(self.__class__,cmd,fn)
            setattr(type(self),cmd,fn)
        else:
            raise TypeError

    def run(self):
        while True:
            cmd=input('commmand: ').strip()
            if cmd.upper() == 'Q':
                return
            print(getattr(self,cmd,self.default)())

    def default(self):
        return 'default'
b=Dispatcher()
b.reg('cmd2',lambda self:22222222)
b.reg('cmd3',lambda self:333333333333)
b.run()
class Dispatcher:
    def __init__(self):
        print('aaaaaaaaaa')
        self._run() # 初始化时就调用实例的_run方法(类的)
        print(self.__dict__)
        print('bbbbbbbbbbbbbbbb')
        # self.b
    def cmd1(self):
        return 'cmd1'
    def cmd2(self):
        return 'cmd2'

    def _run(self):
        while True:
            cmd=input('cmd: ').strip()
            if cmd.lower() == 'q':
                return
            print(getattr(self,cmd,lambda : 'default')())

Dispatcher()
# print(b.__dict__)

 

反射相关的函数和方法

一个Point类,查看它的实例属性,动态增加其属性

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('show:{{{}:{}}}'.format(self.x,self.y))

p=Point(22,33)
print(str(p))
print(p.__dict__)
p.__dict__['y']=44
print(p.__dict__)
p.z=10
print(p.__dict__)
print(dir(p),type(dir(p)))
print(p.__dir__())

上面的例子,访问方式不优雅,Python提供了内置函数

内建函数 意义
getattr(object,name[,default]) 通过name访问object属性,属性不存在,返回default,未设置default,抛出AttributeError,name必须为字符串
setattr(object,name,value) object属性存在,覆盖,不存在,新增
hasattr(object,name) 判断对象是否有name的属性,name必须为str

 

用上面方法重写代码

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)

p1=Point(11,22)
p2=Point(33,44)
print(repr(p1),repr(p2),sep='\n')
print(p1.__dict__)
setattr(p1,'y',999)
setattr(p1,'z',888)
print(getattr(p1,'__dict__'))
print(p1.__dict__)
# 动态调用方法
if hasattr(p1,'show'):
    p1.show()
if hasattr(p1,'uiop'):
    p1.uiop()
#动态增加方法
# 为类增加方法
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) # 未绑定需手动传参self
print(p1)
def fn(self,other):
    self.x=self.x-other.x
    self.y=self.y-other.y
    return self

if hasattr(p1,'sub'):
    setattr(p1,'sub',lambda self,other:exec('self.x=self.x-other.x;self.y=self.y-other.y'))
    # setattr(p1,'sub',fn)

print(p1.sub)
print(p1.sub(p1,p1))
print(p1)

print(p1.__dict__)
print(Point.__dict__)

 

 

 

反射相关的魔术方法

__getattr__       __setattr__         __delattr__

__getattr__:

class Base:
n=00
class Point(Base):
z=6
def __init__(self,x,y):
self.x=x
self.y=y
def show(self):
print(self.x,self.y)
# def __getattr__(self,item):
# return '{} object missing {}'.format(self.__class__.__name__,item)

p1=Point(11,22)
print(p1.x)
print(p1.z)
print(p1.n)
# print(p1.mm)
print(getattr(p1,'uio','uioopp'))
# 使用getattr()时,如果类存在__getattr__魔术方法,可不设置default,如果未设置default,也不存在__getattr__则会抛异常
# 直接访问属性时,没有__getattr__直接抛异常

属性会按照继承关系找,找不到,执行__getattr__方法,如果没有这个方法,抛出AttributeError异常,表示找不到该属性

查找属性顺序为:
instance.__dict__       =>           instance.__class__.__dict__       =>       继承的祖先类(until object).__dict__           =>        调用getattr()

 

__setattr__

 

class Base:
    n=00
class Point(Base):
    z=6
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def show(self):
        print(self.x,self.y)

    def __getattr__(self,item):
        return '{} object missing {}'.format(self.__class__.__name__,item)

    def __setattr__(self,key,value):
        print('setattr {}={}'.format(key,value)) # 覆盖 object的方法后,属性未写到dict中

p1=Point(11,22)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.mm)
p1.x=55
print(p1.__dict__)
p1.__dict__['x']=66 # 直接操作实例自身的属性__dic__不会调用__setattr__
print(p1.__dict__)
print(p1.x)
setattr(p1,'vv',888)
print(p1.__dict__)

实例通过 "." 或setattr设置属性,like self.x='xxxxxxx'       or         setattr(p1,'x','xxxxxxxxxxxx'),就会调用__setattr__()魔术方法,但是直接操作自身的字典,like      p1.__dict__['x']='xxxxxxxxxxxxx'

则不会触发__setattr__的执行

 

class Point(Base):
    z=6
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def show(self):
        print(self.x,self.y)

    def __getattr__(self,item):
        return '{} object missing {}'.format(self.__class__.__name__,item)

    def __setattr__(self,key,value):
        print('setattr {}={}'.format(key,value)) # 覆盖 object的方法后,属性未写到dict中
        self.__dict__[key]=value # 执行写入操作

 

__setattr__方法,可以拦截对实例属性的增加,修改,如果要设置生效,需要手动操作实例的__dict__

 

 

__delattr__

 

class Ore:
    z=55
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __delattr__(self,item): # 实例方法
        print('Can not del {}'.format(item))

p=Ore(11,22)
del p.x
p.z=33
del p.z
print(Ore.__dict__)
print(p.__dict__)
del Ore.z
print(Ore.__dict__)
print(p.__dict__)

可以阻止通过实例删除属性的操作,但是通过类依然可以删除属性,因为这三个魔术方法都是针对实例

 

__getattribute__

 

class Base:n=00
class Ore(Base):
    z=66
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __getattr__(self,item):
        return '{} object missing {}'.format(self.__class__.__name__,item)

    def __getattribute__(self,item):
        return item

o=Ore(11,22)
print(o.__dict__)
print(o.x)
print(o.z)
print(o.mmmm)
print(Ore.__dict__)
print(Ore.z)

实例的所有属性访问,第一个都会调用__getattribute__方法,__getattribute__阻止了实例属性的查找,该方法返回计算后的值或者抛出一个AttributeError异常

它的return值将作为属性查找的结果,如果抛出AttributeError异常,则会直接调用__getattr__方法,表示属性没有找到

class Base:n=00
class Ore(Base):
    z=66
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __getattr__(self,item):
        return '{} object missing {}'.format(self.__class__.__name__,item)

    def __getattribute__(self,item):
        raise AttributeError('Attribute Not Found')
        return item

o=Ore(11,22)
print(o.__dict__)

 

 上图产生循环调用

修改代码

class Base:n=00
class Ore(Base):
    z=66
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __getattr__(self,item):
        return '{} object missing {}'.format(self,item)

    def __getattribute__(self,item):
        raise AttributeError('Attribute Not Found')
        return item

o=Ore(11,22)
print(o.__dict__)
print(o.x)
print(o.z)
print(o.mmmm)
print(Ore.__dict__)
print(Ore.z)
 

 

 所有的实例属性访问,先调用__getattribute__后直接raise AttributeError后调用了__getattr__方法

class Base:n=00
class Ore(Base):
    z=66
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __getattr__(self,item):
        return '{} object missing {}'.format(self,item)

    def __getattribute__(self,item):
        # raise AttributeError('Attribute Not Found')
        # return self.__dict__[item] # 会产生循环递归
        return object.__getattribute__(self,item) # 正确的返回方法

o=Ore(11,22)
print(o.__dict__)
print(o.x)
print(o.z)
print(o.mmmm)
print(Ore.__dict__)
print(Ore.z)

 

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

魔术方法 意义
__getattr__ 通过搜索实例,实例的类,以及祖先类查不到该属性时调用
__setattr__ 通过 "." 或 setattr(object,key,value)访问实例属性,进行增加,修改时调用
__delattr__ 通过实例删除属性时调用
__getattribute__ 实例的所有属性调用都从此方法起始

 

属性查找顺序:

__getattribute__()    =>       instance.__dict__          =>            instance.__class__.__dict__           =>          祖先类(until object).__dict__                 =>           __getattr__()

 

posted @ 2020-10-07 12:59  ascertain  阅读(160)  评论(0编辑  收藏  举报