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__()  实例的所有属性调用都从这个方法开始

猜你喜欢

转载自www.cnblogs.com/rprp789/p/9690745.html
9.4