1. 主页
  2. Python基础到高级
  3. 反射

反射

反射的定义

反射就是在运行时获取类的信息

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__

我们要如何帮助您?

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注