反射的定义
反射就是在运行时获取类的信息
In [1]: class Point:
...: def __init__(self, x, y):
...: self.x = x
...: self.y = y
...: def print(self, x, y):
...: print(x,y)
...:
In [2]: p = Point(3,5) # 实例化一个类
In [3]: p.__dict__ # 获取实例的属性
Out[3]: {'x': 3, 'y': 5}
In [4]: p.__dict__['z'] = 10
In [5]: p.z
Out[5]: 10
p.__dict__、p.z这些都是反射
我们通过dir获取到的所有的属性和方法,都是反射
getattr、setattr、hasattr
getattr、setattr、hasattr是和反射息息相关的三个方法
getattr的使用详解及参数介绍
In [6]: p.__dict__['z'] # 对于属性来说,可以通过__dict__获取,但是成员方法是不行的
Out[6]: 10
In [7]: getattr(p, 'print')(3,5) # 成员方法无法通过__dict__获取,但是可以通过getattr函数获取
3 5
In [14]: getattr(p, 'x') # getattr 也可以获取到属性的
Out[14]: 3
getattr(object, name[, default]) -> value
object: 对象的名称
name: 方法的名称
default: 默认值,可不写
setattr的使用详解及参数介绍
In [17]: setattr(p, 'a', 'set_a') # 给对象p定义一个名字a,值为set_a的属性
In [18]: p.a
Out[18]: 'set_a'
setattr(obj, name, value, /)
obj: 对象的名称
name: 需要添加的成员名称
value:成员的value值
setattr对象是实例,直接使用setattr给对象添加方法是不可行的
可以结合types模块中MethodType方法实现动态添加方法
给实例动态添加方法,需要先把函数转换为方法,而types模块中MethodType方法就可以实现把函数转换为方法
In [23]: def test(self): # 创建一个需要添加到实例中的函数模板
...: print(self.x)
...:
In [24]: import types
In [25]: setattr(p, 'test', types.MethodType(test,p)) # 结合types.MethodType方法动态给实例添加方法
In [26]: p.test
Out[26]: <bound method test of <__main__.Point object at 0x7fca09839b38>>
In [27]: p.test() # 查看是否添加成功
3
hasattr的使用详解及参数介绍
In [20]: hasattr(p, 'a') # 判断p对象有没有a成员,True是有,False是没有
Out[20]: True
In [21]: hasattr(p, 'lei') # 判断p对象有没有lei成员,True是有,False是没有
Out[21]: False
hasattr(obj, name, /)
obj: 对象的名称
name: 需要判断是否存在的成员的名称
__getattr__、__setattr__、__delattr__
__getattr__、__setattr__、__delattr__是三个魔术方法
当一个类定义了__getattr__方法时,如果访问不存在的成员,会调用__getattr__方法
In [28]: class A:
...: def __init__(self):
...: self.x = 3
...: def __getattr__(self, name): # 给类定义__getattr__方法
...: return 'missing property {}'.format(name)
...:
In [29]: a = A()
In [30]: a.x # 当访问存在的属性的时候,则获取属性值
Out[30]: 3
In [31]: a.y # 当调用不存在的属性的时候,则调用__getattr__方法
Out[31]: 'missing property y'
当类定义了__getattr__方法的时候,成员的查找顺序是__dict__ -> __class__ -> __getattr__
当类实现了__setattr__时,任何地方对这个类的对象添加属性,或者对现有属性赋值,都会调用__setattr__包括在__init__中给对象赋值
In [37]: class A:
...: def __init__(self):
...: self.x = 3
...: def __setattr__(self,name ,value): # 定义了__setattr__方法
...: print('set {} to {}'.format(name, value))
...: self.__dict__[name] = value # 当定义了__setattr__方法的时候,也同时有__init__方法的时候,若是想在初始化的时候定义的属性信息正常加入__dict__的话,需要执行这样代码
...:
In [38]: a = A()
set x to 3
In [39]: a.y = 5
set y to 5
In [40]: a.__dict__
Out[40]: {'x': 3, 'y': 5}
当需要对实例属性的修改做一些额外的操作的时候,可以使用__setattr__
同时__setattr__在使用的同时注意的地方有很多,可能在你不经意间就自己给自己挖坑了,所以尽量少的使用__setattr__
当一个实现了**__delattr__**方法时,删除其实例的属性,会调用此方法
In [44]: class A:
...: def __init__(self):
...: self.x = 3
...: def __delattr__(self,name): # 定义__delattr__方法
...: print('you can not delete property: {}'.format(name))
...:
In [45]: a = A()
In [46]: a.x
Out[46]: 3
In [47]: del a.x # 删除对象属性的时候执行__delattr__方法,表示不能删除对象属性
you can not delete property: x
In [48]: a.x # 检查对象属性还是可以调用的
Out[48]: 3
__getattribute__
__getattribute__是针对实例的一种优先级最高的方法,当对象实例化后,每次调用对象的成员的时候,都会首先执行__getattribute__操作,非常的不可控,因此也是尽量避免使用
In [52]: class A:
...: NAME = 'A'
...: def __init__(self):
...: self.x = 3
...: def __getattribute__(self,name):
...: return 'lanyulei'
...: def method(self):
...: print('method')
...:
In [53]: a = A()
In [54]: a.NAME # 调用NAME属性,返回了\_\_getattribute\_\_方法的返回值
Out[54]: 'lanyulei'
In [55]: a.x # 调用x属性,返回了\_\_getattribute\_\_方法的返回值
Out[55]: 'lanyulei'
In [57]: a.method # 调用method方法,返回了\_\_getattribute\_\_方法的返回值
Out[57]: 'lanyulei'
当对象中出现了__getattribute__的方法的时候,那么对象内成员的调用顺序就再次发生了改变:
__getattribute__ -> __dict__ -> __class__ -> __getattr__