Python-面向对象魔术方法之反射

反射

概述

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

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

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

简单说,在Python中,能够通过一个对象,找到其type、class、attribute或method的能力,称之为反射或自省

具有反射能力的函数有type()、isinstance()、called()、dir()、getatter()等

反射相关的函数和方法

需求,有一个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(self.x, self.y)

p = Point(4, 5)
print(p)
print(p.__dict__)
p.__dict__['y'] = 10
print(p.__dict__)

p.z = 20
print(p.__dict__)
print(dir(p))
print(p.__dir__())
print(Point.__dict__)

#输出
<Point 4,5>
{'x': 4, 'y': 5}
{'x': 4, 'y': 10}
{'x': 4, 'y': 10, 'z': 20}
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'show', 'x', 'y', 'z']
['x', 'y', 'z', '__module__', '__init__', '__str__', 'show', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
{'__module__': '__main__', '__init__': <function Point.__init__ at 0x0000021625775AE8>, '__str__': <function Point.__str__ at 0x0000021625775B70>, 'show': <function Point.show at 0x000002162579AD08>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None}

上例通过属性字典__dict__来访问对象的属性,本质上也是利用反射的能力。 

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

内建函数 意义
getattr(object,name[,default] 通过name返回object的属性值,当属性不存在,将使用default返回,如果没有default,则抛出AttributeError,name必须是字符串
setattr(object,name,value) object的属性存在,则覆盖,不存在则新增
hasattr(object,name) 判断对象是否有这个名字的属性,name必须为字符串

用上面的方法修改上列中的代码

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(4,5)
p2 = Point(10,20)

print(repr(p1),repr(p2))
print(p1.__dict__)
setattr(p1,'y',16)
setattr(p1,'z',100)
print(p1.__dict__)
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)
print(p1.sub(p1,p2))

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

#输出
<__main__.Point object at 0x00000172971D82E8> <__main__.Point object at 0x00000172971BD2E8>
{'x': 4, 'y': 5}
{'x': 4, 'y': 16, 'z': 100}
{'x': 4, 'y': 16, 'z': 100}
<Point 4,16>
<function <lambda> at 0x00000172971A5950>
<bound method <lambda> of <__main__.Point object at 0x00000172971D82E8>>
<Point 14,36>
<function <lambda> at 0x00000172971CB6A8>
<Point -6,-4>
{'x': 4, 'y': 16, 'z': 100, 'sub': <function <lambda> at 0x00000172971CB6A8>}
{'__module__': '__main__', '__init__': <function Point.__init__ at 0x00000172971A5AE8>, '__str__': <function Point.__str__ at 0x00000172971A5B70>, 'show': <function Point.show at 0x00000172971CBD08>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None, 'add': <function <lambda> at 0x00000172971A5950>}

思考

这种动态增加属性的方式和装饰器装饰一个类、Mixin方式的差异?

这种是动态增删属性的方法,是运行时改变类或实例的方式,但是装饰器或Mixin都是定义时就决定了,因此反射的能力更加的灵活。

反射相关的魔术方法

__getattr__()、__setattr__()、__delattr__()这三个魔术方法,分别测试这三个方法

猜你喜欢

转载自www.cnblogs.com/alrenn/p/13192781.html