python——反射

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__()

原文地址:https://www.cnblogs.com/weststar/p/11427903.html