反射简介
反射就是通过字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动。简单说,在Python中,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或自省。
具有反射能力的函数有type(),isinstance(),getattr()等。
可使用反射的地方:
- 反射类中的变量:静态属性,类方法,静态方法;
- 反射对象中的变量、对象属性、普通方法;
- 反射模块中的变量;
- 反射本文件中的变量。
为了方便阐述反射的用法,先定义一个类:
class Point:def __init__(self, x, y):self.x = xself.y = ydef __str__(self):return "{} and {}".format(self.x, self.y)def show(self):print(self.x, self.y)
先对类实例化,然后看一下__dict__方法。
>>> p = Point(4, 5)>>> print(p)4 and 5>>> print(p.__dict__){'x': 4, 'y': 5}>>> p.__dict__['y'] = 16>>> print(p.__dict__){'x': 4, 'y': 16}
上例通过属性字典__dict__来访问对象的属性,本质上就是利用反射的能力,但是上面的例子中,访问的方式不优雅,Python提供了内置的函数。
反射用法
getattr
判断类、对象或者模块中是否有相应的属性或方法。
用法:
getattr(obj, str, default=None)
判断obj中是否有str属性,有就返回,没有时如果有传入第三参数就返回第三参数,没有就报错。
例子1:下面两个函数等价。
>>> print(p.__dict__){'x': 4, 'y': 16}>>> print(getattr(p, '__dict__')){'x': 4, 'y': 16}
这验证了上面说的__dict__本质上就是利用反射的能力。
例子2:getattr()使用方法
>>> p = Point(4, 5)>>> getattr(p, "x")4>>> getattr(p, "y")16>>> print(getattr(p, '__dict__')){'x': 4, 'y': 5}>>> getattr(p, "z", "NotFound")'NotFound'>>> getattr(p, "z")Traceback (most recent call last):File "D:\Anaconda3\lib\site-packages\IPython\core\interactiveshell.py", line 3331, in run_codeexec(code_obj, self.user_global_ns, self.user_ns)File "<ipython-input-15-83b506d04926>", line 1, in <module>getattr(p, "z")AttributeError: 'Point' object has no attribute 'z'
settattr
设置属性object的属性,存在则覆盖,不存在则新增。第三参数为新的属性值。
setattr(object, name, value)
例子:settattr()使用方法
>>> p1 = Point(4, 5)>>> p2 = Point(10, 10)>>> print(p1.__dict__){'x': 4, 'y': 5}>>> print(p2.__dict__){'x': 10, 'y': 10}>>> setattr(p1, 'y', 16)>>> setattr(p1, 'z', 10)>>> print(p1.__dict__){'x': 4, 'y': 16, 'z': 10}
hasattr
判断对象是否有这个名字的属性,有就返回True,没有就返回False。name必须为字符串
hasaattr(object,name)
例子:动态调用方法
>>> if hasattr(p1, 'show'):>>> print(getattr(p1, 'show'))<bound method Point.show of <__main__.Point object at 0x0000014572376488>>
动态增加方法
>>> if not hasattr(Point, 'add'):>>> setattr(Point, 'add', lambda self, other: Point(self.x + other.x, self.y + other.y))>>> print(Point.add)<function <lambda> at 0x0000014572371558>>>> print(p1.add)<bound method <lambda> of <__main__.Point object at 0x0000014572376488>>>>> print(p1.add(p2))14 and 26
>>> if not hasattr(p1, 'sub'):>>> setattr(p1, 'sub', lambda self, other: Point(self.x - other.x, self.y - other.y))>>> print(p1.sub(p1, p2))-6 and 6>>> print(p1.__dict__){'x': 4, 'y': 16, 'z': 10, 'sub': <function <lambda> at 0x0000014572381DC8>}>>> print(Point.__dict__){'__module__': '__main__', '__init__': <function Point.__init__ at 0x00000145722E04C8>, '__str__': <function Point.__str__ at 0x00000145722E0318>, 'show': <function Point.show at 0x00000145722E0288>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None, 'add': <function <lambda> at 0x0000014572371558>}
反射相关的魔术方法
getattr()
class Base:n = 0class Point(Base):z = 6def __init__(self, x, y):self.x = xself.y = ydef show(self):print(self.x, self.y)def __getattr__(self, item):return item>>> p1.x4>>> p1.z6>>> p1.n0>>> p1.t't'
实例属性会按照继承关系寻找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttributeError异常标识找不到属性。
setattr()
class Base:n = 0class Point(Base):z = 6def __init__(self, x, y):self.x = xself.y = ydef show(self):print(self.x, self.y)def __getattr__(self, item):return itemdef __setattr__(self, key, value):print(key, value)# -------------------------------------------------->>> p1 = Point(4, 5)x 4y 5>>> print(p1.x)x>>> print(p1.z)6>>> print(p1.n)0>>> print(p1.t)t# -------------------------------------------------->>> p1.x = 50>>> print(p1.x)x>>> print(p1.__dict__){}>>> p1.__dict__['x'] = 60>>> print(p1.__dict__){'x': 60}>>> p1.x60
实例通过.点号设置属性,例如self.x=x,就会调用__setattr__(),属性要加到实例的__dict__中,就需要自己完成。
setattr()方法,可以拦截堆实例属性的增加,修改操作,如果要设置生效,需要自己操作实例的__dict__。
class Base:n = 200class A(Base):z = 100d = {}def __init__(self, x, y):self.x = xsetattr(self, 'y', y)self.__dict__['a'] = 5def __getattr__(self, item):print(item)return self.d[item]def __setattr__(self, key, value):print(key, value)self.d[key] = value>>> a = A(4, 5)x 4y 5>>> print(a.__dict__){'a': 5}>>> print(A.__dict__)A.__dict__mappingproxy({'__module__': '__main__','z': 100,'d': {'x': 4, 'y': 5},'__init__': <function __main__.A.__init__(self, x, y)>,'__getattr__': <function __main__.A.__getattr__(self, item)>,'__setattr__': <function __main__.A.__setattr__(self, key, value)>,'__doc__': None})>>> print(a.x, a.y)xy4 5>>> print(a.a)5
delattr()
class Point:z = 5def __init__(self, x, y):self.x = xself.y = ydef __delattr__(self, item):print(item)p = Point(14, 5)>>> p = Point(3, 4)>>> del p.xx>>> p.z=15>>> del p.zz>>> del p.ZZ>>> print(Point.__dict__){'__module__': '__main__', 'z': 5, '__init__': <function Point.__init__ at 0x0000019E93B01318>, '__delattr__': <function Point.__delattr__ at 0x0000019E93B013A8>, '__dict__': <attribute '__dict__' of 'Point' objects>, '__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None}
getattribute
class Base:n = 0class Point(Base):z = 6def __init__(self, x, y):self.x = xself.y = ydef __getattr__(self, item):return itemdef __getattribute__(self, item):return item>>> p1 = Point(4, 5)>>> print(p1.__dict__)__dict__>>> print(p1.x)x>>> print(p1.z)z>>> print(p1.n)n>>> print(p1.t)t>>> print(Point.__dict__){'__module__': '__main__', 'z': 6, '__init__': <function Point.__init__ at 0x000001F5EB7063A8>, '__getattr__': <function Point.__getattr__ at 0x000001F5EB706558>, '__getattribute__': <function Point.__getattribute__ at 0x000001F5EB706168>, '__doc__': None}>>> print(Point.z)6
实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回值或者抛出一个AttributeError异常。
- 该方法的返回值将作为属性查找的结果。
- 如果抛出
AttributeError异常,则会直接调用__getattr__方法,因为属性没有找到,__getattribute__方法中为了避免在该方法中无限递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性。
需要注意的是,除非明确知道**__getattrtbute__**方法用来做什么,否则不要使用。
使用场景
- input
用户输入的如果是a,那么就打印1,如果输入的是b就打印2 - 文件
从文件中读出的字符串,想转换成变量的名字 - 网络
将网络传输的字符串转换成变量的名字
