1、概述
运行时,区别于翻译时,指的是程序被加载到内存中执行的时候。
反射,reflection,指的是运行时获取类型定义信息。
一个对象能够在运行时,像照镜子一样,反射出其类型信息。简单说,在Python中,能够通过一个对象,找出其type、class、attribute或method的能力,称为反射或自省。
具有反射能力的函数有type(),isinstance(),callable().dir().getattr()等
2、反射相关函数的用法
1 class Point: 2 '''反射函数用法举例''' 3 name = 'alex' 4 def __init__(self, x, y): 5 self.x = x 6 self.y = y 7 def __str__(self): 8 return "{} and {}".format(self.x, self.y) 9 def show(self): 10 print(self.x, self.y) 11 12 p = Point(4, 5) 13 print(p) # 触发__str__() 输出:4 and 5 14 print(p.__dict__) # 不会有类变量和方法存在,只存在私有属性,可以清楚实例的机制 输出:{'x': 4, 'y': 5} 15 print(Point.__dict__) 16 # 修改实例的属性 17 p.__dict__['y'] = 15 18 print(p.__dict__) #输出:{'x': 4, 'y': 15} 19 # 增加实例的属性 20 p.z = 10 21 print(p.__dict__) # 输出:{'x': 4, 'y': 15, 'z': 10} 22 # 对象可进行的操作 23 print(dir(p))
上例通过属性字典__dict__来访问对象的属性,本质上就是利用反射的能力(可以将字典理解为一种反射),但是上面的例子中,访问的方式不优雅,Python提供了内置的函数
3、反射函数
内建函数 | 意义 |
getattr(object,name[,default]) | 通过name返回object的属性值,当属性不存在,当属性不存在,将使用default返回,如果没有default,则抛出AttributeError,name必须是字符串 |
setattr(object,name,value) | 若存在object.name这是属性,则覆盖,不存在则新增 |
hasattr(object,name | 判断对象是否具有这个名字的属性,返回bool值,name必须为字符串 |
delattr(object,name) | 用于删除对象的属性,相当于del object.name |
getattr()和hasattr()
(1)动态调用方法和增添方法
1 def bulk(self): 2 print('%s is yelling...' % self.name) 3 class Dog(object): 4 num = 123 5 def __init__(self, name): 6 self.name = name 7 8 def eat(self, food): 9 print('%s is eating %s' % (self.name, food)) 10 11 obj = Dog('alex') 12 13 for i in range(2): 14 choice = input('>>>').strip() 15 # 动态调用方法 16 if hasattr(obj, choice): 17 func = getattr(obj, choice) 18 func('hotdog') 19 # 增添方法 20 else: 21 setattr(obj, choice, bulk) 22 func = getattr(obj, choice) 23 func(obj)
执行结果:
>>>eat alex is eating hotdog >>>talk alex is yelling...
(2)动态调用属性和增添属性
1 for i in range(2): 2 choice = input('>>>') 3 #动态调用属性 4 if hasattr(obj, choice): 5 argu = getattr(obj, choice) 6 print('%s=%s'%(choice,argu)) 7 else: 8 setattr(obj, choice, 5) 9 argu_new = getattr(obj, choice) 10 print('{}={}'.format(choice, argu_new))
执行结果:
>>>num num=123 >>>age age=5
delattr()删除属性或方法
1 >>> class Myclass: 2 ... num = 2 3 ... def __init__(self,name): 4 ... self.name=name 5 ... def func(self): 6 ... print('This is func') 7 ... 8 >>> obj = Myclass('alex') 9 >>> obj.num 10 2 11 >>> obj.func() 12 This is func 13 >>> delattr(obj,'num') # num是类变量只能通过类去删除,在实例对象中不存在num这一个变量 14 Traceback (most recent call last): 15 File "<stdin>", line 1, in <module> 16 AttributeError: num 17 >>> obj.num 18 2 19 >>> delattr(Myclass,'num') # 删除属性num 20 >>> obj.num 21 Traceback (most recent call last): 22 File "<stdin>", line 1, in <module> 23 AttributeError: 'Myclass' object has no attribute 'num' 24 >>> delattr(Myclass,'func') # 删除方法 25 >>> func() 26 Traceback (most recent call last): 27 File "<stdin>", line 1, in <module> 28 NameError: name 'func' is not defined
4、在类中使用内建反射函数(慎用,和直接外部使用反射函数有所不同)
(1)getattr(object,name[,default])
1 class Base(object): 2 n = 0 3 class Point(Base): 4 z = 6 5 def __init__(self, x, y): 6 self.x = x 7 self.y = y 8 def show(self): 9 print(self.x, self.y) 10 def __getattr__(self, item): 11 return item 12 13 p = Point(4,5) 14 print('p.x>>>>', p.x) 15 print('p.y>>>', p.y) 16 print('p.n>>>', p.n) 17 print('p.t>>>', p.t)
执行结果:
1 p.x>>>> 4 2 p.y>>> 5 3 p.n>>> 0 4 p.t>>> t
实例属性会按照继承关系寻找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttributeError异常标识找不到属性
查找属性顺序为:
p.__dict__---->继承的祖先类(直到object)的__dict__—>找不到—>调用getattr()
2、setattr(object,name,value)
1 class Base: 2 n = 0 3 class Point(Base): 4 z = 6 5 def __init__(self, x, y): 6 self.x = x 7 self.y = y 8 def show(self): 9 print(self.x, self.y) 10 def __getattr__(self, item): 11 return item 12 def __setattr__(self, key, value): 13 print('in the setattr: key->{} value->{},'.format(key,value))
(1)实例化对象:
p1 = Point(4, 5) print(p1.__dict__) 执行结果 in the setattr: key->x value->4, in the setattr: key->y value->5, {}
注释掉第12、13行代码
p1 = Point(4, 5) print(p1.__dict__) 执行结果: {'x': 4, 'y': 5}
结论:我们可以看出,__setattr__()函数会先于__init__()执行,且并不会在实例化对象开辟的内存中保存,而是单独开辟了内存,保存变量初始化的值。其实执行顺序是先执行__setattr__(),再执行__init__(),
只是变量的初始化已经完成了,不会再在实例对象的内存中保存实例变量了。
(2)属性查找顺序
1 print(p1.x, p1.y) 2 print(p1.z) 3 print(p1.n) 4 执行结果: 5 x y 6 6 7 0
结论:其实我们变量的初始化函数根本就没有生效,在实例对象p1.__dict__什么都没有保存进去,应为setattr()另外开辟地址进行存储啦。所以找查找属性x,y时,先在实例属性中查找——》在类中查找——》在父类中查找——》找不到,触发__getattr__().
1 p1.x = 50 2 print(p1.x) 3 print(p1.__dict__) 4 执行结果>>> 5 key->x value->50, 6 x 7 {}
(3)在设置属性的时候,属性需要加到实例的__dict__中,才会生效。
1 p1.__dict__['x']=60 2 print(p1.__dict__) 3 print(p1.x) 4 执行结果>>> 5 {'x': 60} 6 60
3.__delattr__()
1 class Point: 2 z = 5 3 def __init__(self, x, y): 4 self.x = x 5 self.y = y 6 def __delattr__(self,item): 7 print(item) 8 p = Point(14,5) 9 del p.x 10 p.z = 15 11 del p.z 12 del p.Z 13 print(Point.__dict__)
执行结果:
x z Z {'__module__': '__main__', 'z': 5, '__init__': <function Point.__init__ at 0x000002049C0A7620>,
'__delattr__': <function Point.__delattr__ at 0x000002049C0A76A8>, '__dict__': <attribute '__dict__' of 'Point' objects>,
'__weakref__': <attribute '__weakref__' of 'Point' objects>, '__doc__': None}
结论:其实我们并未删除实例对象中的实例变量,我们最后可以看到p.__dict__的内容{'x': 14, 'y': 5},这说明del x操作只能触发__delattr_()函数,实际并没有完成相应的功能。
4、getattribute()
1 class Base: 2 n=0 3 class Point(Base): 4 z=6 5 def __init__(self,x,y): 6 self.x=x 7 self.y=y 8 def __getattr__(self,item): 9 return item 10 def __getattribute__(self,item): 11 return item 12 p1=Point(4,5) 13 print(p1.__dict__) 14 print(p1.x) 15 print(p1.z) 16 print(p1.n) 17 print(p1.t) 18 print(Point.__dict__) 19 print(Point.z)
执行结果:
__dict__ x z n t {'__module__': '__main__', 'z': 6, '__init__': <function Point.__init__ at 0x000001F1C07C7620>,
'__getattr__': <function Point.__getattr__ at 0x000001F1C07C76A8>, '__getattribute__': <function Point.__getattribute__ at 0x000001F1C07C7730>, '__doc__': None} 6
实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回值或者抛出一个AttributeError异常
它的return值将作为属性查找的结果
如果抛出AttributeError异常,则会直接调用__getattr__方法,因为属性没有找到
__getattribute__方法中为了避免在该方法中无线递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,需要注意的是,除非明确知道__getattrtbute__方法用来做什么,否则不要使用
属性查找顺序:
实例调用__getattribute__()—>instance.dict–>instance.class.dict–>继承的祖先类(直到object)的__dict__–>调用__getattr__()