一、概述
一般的高阶语言都有反射的功能特性,python也不例外,网上资料显示,python支持类反射和模块反射,今天就先学习一下类反射的相关知识,模块反射后续再展开把。Python的类反射用于把字符串(对应于属性或方法)反射成内存中的地址对象,以便按需调用,实现某些特性的动态装配,它主要通过hasattr()、getattr()、setattr()、和delattr()来实现,类似于数据库的增删改查操作把。
二、反射函数
以下内容主要思想转自师兄张其高的博客https://www.cnblogs.com/zhangqigao/articles/6947023.html
2.1 hasattr(obj, name_str)
作用:判断一个对象obj中是否有对应的name_str字符串所代表的属性或者方法,返回布尔值。注意这里的对象可以是类,也可以是实例化的对象。
1 class Dog(object): 2 3 def __init__(self, name): 4 self.name = name 5 6 def eat(self, food): 7 print('%s is eating %s' % (self.name, food)) 8 9 obj1 = Dog('XiaoHuang') 10 11 print('obj1: %s ' % hasattr(obj1, 'name')) #判断属性 12 print('class Dog: %s' % hasattr(Dog, 'eat')) #判断方法 13 14 input = input(">>>:").strip() #动态输入字符串,动态判断 15 print(hasattr(obj1, input)) 16 17 输出: 18 obj1: True 19 class Dog: True 20 >>>:food 21 False
2.2 getattr(obj, name_str)
作用:根据字符串name_str获取obj对象中的对应方法的内存地址或者对应属性的值
1 class Dog(object): 2 3 def __init__(self, name): 4 self.name = name 5 6 def eat(self, food): 7 print('%s is eating %s' % (self.name, food)) 8 9 obj1 = Dog('XiaoHuang') 10 obj2 = Dog('Bark') 11 12 print('obj1: %s ' % getattr(obj1, 'name')) #返回属性地址 13 print('class Dog: %s' % getattr(Dog, 'eat')) #返回方法地址 14 print('obj1: %s' % getattr(obj1, 'eat')) #返回方法地址 15 print('obj2: %s' % getattr(obj2, 'eat')) 16 17 结果输出: 18 obj1: XiaoHuang 19 class Dog: <function Dog.eat at 0x00000000021CEEA0> 20 obj1: <bound method Dog.eat of <__main__.Dog object at 0x00000000021CCCC0>> 21 obj2: <bound method Dog.eat of <__main__.Dog object at 0x00000000021CCCF8>>
这里有一个小疑问,既然类中的方法都保存在类中,并且只保存一份,这里通过obj1和obj2去获取的类方法的地址显示是不同的,这是为什么呢?原因待脑补后补充。
2.3 setattr(obj, name_str, value)
作用:给obj对象添加一个新属性或者新方法,setattr(x, 'y', v) is equivalent to ``x.y = v''
- 给对象新增一个方法View Code
1 class Dog(object): 2 3 def __init__(self, name): 4 self.name = name 5 6 def eat(self, food): 7 print('%s is eating %s' % (self.name, food)) 8 9 def bulk(self): #预期新增的方法 10 print('%s is yelling...' % self.name) 11 12 obj1 = Dog('XiaoHuang') 13 14 str_input = input(">>>:").strip() 15 setattr(obj1, str_input, bulk) 16 func = getattr(obj1, str_input) 17 func(obj1) 18 19 print('obj1: %s ' % getattr(obj1, str_input)) 20 print('class Dog: %s' % getattr(Dog, str_input)) 21 22 结果输出: 23 >>>:hehe 24 XiaoHuang is yelling... 25 obj1: <function bulk at 0x0000000001CF3E18> 26 Traceback (most recent call last): 27 File "D:/python/S13/Day6/testclass.py", line 28, in <module> 28 print('class Dog: %s' % getattr(Dog, str_input)) 29 AttributeError: type object 'Dog' has no attribute 'hehe' 30 31 Process finished with exit code 1
(1) 这里新增的方法只是对实例化的对象有效,对类无效
(2) 最后在获取func的内存地址后,也就是bulk的内存地址后,调用时需要传入实例化对象方法,因为定义的新增bulk方法有参数self,而这种动态新增的方法,参数无论是否为self,都需要手动传入 - 给对象新增一个属性View Code
1 # !/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 class Dog(object): 5 6 def __init__(self, name): 7 self.name = name 8 9 def eat(self, food): 10 print('%s is eating %s' % (self.name, food)) 11 12 13 obj1 = Dog('XiaoHuang') 14 15 str_input = input(">>>:").strip() 16 setattr(obj1, str_input, 3) 17 print(getattr(obj1, str_input)) 18 print('obj1: %s ' % getattr(obj1, str_input)) 19 print('----') 20 print(obj1.__dict__) 21 22 输出: 23 >>>:age 24 3 25 obj1: 3 26 ---- 27 {'name': 'XiaoHuang', 'age': 3}
上述示例程序显示,对象新增的属性可以通过__dict__来查看到,新增成功。
2.4 delattr(obj, name_str)
作用:删除obj对象中的属性或者方法,delattr(x, 'y') is equivalent to ``del x.y''
1 class Dog(object): 2 3 def __init__(self, name): 4 self.name = name 5 6 def eat(self, food): 7 print('%s is eating %s' % (self.name, food)) 8 9 10 obj1 = Dog('XiaoHuang') 11 12 str_input = input(">>>:").strip() 13 delattr(obj1, str_input) 14 print(Dog.__dict__) 15 print('----') 16 print(obj1.__dict__) 17 print(getattr(obj1, str_input)) 18 19 输出: 20 #删除属性 21 >>>:name 22 Traceback (most recent call last): 23 File "D:/python/S13/Day6/testclass.py", line 24, in <module> 24 print(getattr(obj1, str_input)) 25 AttributeError: 'Dog' object has no attribute 'name' #访问被删除的name属性报错 26 {'__module__': '__main__', '__init__': <function Dog.__init__ at 0x000000000219EEA0>, 'eat': <function Dog.eat at 0x000000000219EF28>, '__dict__': <attribute '__dict__' of 'Dog' objects>, '__weakref__': <attribute '__weakref__' of 'Dog' objects>, '__doc__': None} 27 ---- 28 {} #实例的唯一属性name不见了 29 30 #删除方法 31 >>>:eat 32 Traceback (most recent call last): 33 File "D:/python/S13/Day6/testclass.py", line 20, in <module> 34 delattr(obj1, str_input) 35 AttributeError: eat #方法删除后立马报错了 36
三、综合运用
上面主要讲述的属性或方法的动态增、删、改、查的基本用法,实际上删除和增加用的很少,这里结合查、删、改来简述一下综合运用。
1 # !/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 class Dog(object): 5 6 def __init__(self, name): 7 self.name = name 8 9 def eat(self, food): 10 print('%s is eating %s' % (self.name, food)) 11 12 13 obj1 = Dog('XiaoHuang') 14 15 str_input = input(">>>:").strip() 16 if hasattr(obj1, str_input): 17 print('Before change') 18 print('obj1 %s:%s' % (str_input, getattr(obj1, str_input))) 19 print('--------') 20 setattr(obj1, str_input, 'Jinba') 21 print('After change') 22 print('obj1 %s:%s' %(str_input, getattr(obj1, str_input))) 23 else: 24 setattr(obj1, str_input, None) #对于不存在的属性设置为None,感觉意义不大? 25 print(getattr(obj1, str_input)) 26 27 输出: 28 >>>:name 29 Before change 30 obj1 name:XiaoHuang 31 -------- 32 After change 33 obj1 name:Jinba 34 35