Python反射机制
我记得以前学习Java的时候,就接触到了反射的概念,后来随着工作,经常听到反射的概念,今天决定好好总结一下。
下面3篇博客我感觉写的很不错,大家可以进行参考。
https://blog.csdn.net/qq_37267015/article/details/71406953
http://www.mamicode.com/info-detail-1401422.html
https://www.cnblogs.com/huxi/archive/2011/01/02/1924317.html
首先大家需要知道反射的概念,比较经典的解释分为以下几种:
- 通过字符串的形式来寻找或操作对象内部的属性叫做反射
- 通过字符串映射或者修改程序运行时的状态、属性或者方法叫做反射
- 反射是通过字符串的形式操作对象相关的成员,反射的的本质其实就是利用字符串的形式去对象(模块)中操作(查找/获取/删除/添加)成员,一种基于字符串的事件驱动!
在python当中一切事物皆对象,所以都可以使用反射。
python中的反射功能涉及到四个内置函数:hasattr、getattr、setattr、delattr,这四个函数分别用于对对象内部执行:检查是否含
有某个成员、获取成员、设置成员、删除成员。
示例程序1:访问成员的两种方式:obj.与obj.__dict__[‘成员‘]
#!/usr/bin/python # -*- coding:utf-8 -*- class Student(object): def __init__(self,name,age): self.name = name self.age = age def study(self,name): print(‘%s is studing‘%self.name) student = Student(‘angela‘,25) print(student.__dict__) #查看student的成员属性(名称空间对应的成员) print(vars(student)) #查看student的成员属性(名称空间对应的成员) #通过字符串直接访问对象所对应的成员. print(student.__dict__[‘name‘],student.__dict__[‘age‘]) print(student.__dict__.get(‘name‘)) print(student.__dict__.get(‘age‘)) #当然,我们一般都是这样子的,但是上面的那种方式是根本 print(student.name,student.age)
运行结果:
{‘name‘: ‘angela‘, ‘age‘: 25} {‘name‘: ‘angela‘, ‘age‘: 25} angela 25 angela 25 angela 25 Process finished with exit code 0
Python当中的反射涉及到了四个主要方法:
hasattr(obj,name_str):判断对象obj是否含有名为name_str的方法或者静态属性,返回值为布尔值,即通过字符串的形式来判断对
象内部是否含有某个属性。
getattr(obj,name_str):根据字符串name_str去获取对象obj当中对应函数的内存地址或者静态属性对应的数值,如果返回值为函数
的内存地址,需要加上括号才能够调用。
setattr(obj,name_str,value): 添加或者修改obj对象当中名为name_str的属性或者方法的数值。
delattr(obj,name_str): 删除obj对象当中名为name_str的属性。
示例程序:Python当中的反射对应的四个方法
#!/usr/bin/python # -*- coding:utf-8 -*- class Student(object): def __init__(self,name,age): self.name = name self.age = age def study(self,name): print(‘%s is studing‘%self.name) student = Student(‘angela‘,25) print(student.__dict__) #查看student的成员属性(名称空间对应的成员) #检查对象是否含有某个成员. print(hasattr(student,‘name‘)) print(hasattr(student,‘study‘)) #通过字符串获取对象当中某个成员对应的数值. func = getattr(student,‘study‘) print(func) func(student) #通过字符串设置或增加对象当中某个成员的数值 setattr(student,‘name‘,‘Jack‘) setattr(student,‘salary‘,2000) print(student.__dict__) #通过字符串删除对象当中的某个成员. delattr(student,‘name‘) delattr(student,‘salary‘) print(student.__dict__) #设置成员的时候还可以增加方法成员. setattr(student,‘show‘,lambda num:num+1) print(vars(student))
运行结果:
{‘name‘: ‘angela‘, ‘age‘: 25} True True <bound method Student.study of <__main__.Student object at 0x000000000219D400>> angela is studing {‘salary‘: 2000, ‘name‘: ‘Jack‘, ‘age‘: 25} {‘age‘: 25} {‘show‘: <function <lambda> at 0x0000000001E5CBF8>, ‘age‘: 25} Process finished with exit code 0
示例程序:在Python当中,万物皆对象,所以只要是对象,就可以使用反射。
accout.py包含的内容:
#!/usr/bin/python # -*- coding:utf-8 -*- import sys #在Python当中,一切皆对象:只要是对象,就可以使用反射机制. class Person(object): def __init__(self,name,age): self.name = name self.age = age def eat(self,name): print(‘%s is eating‘%self.name) if __name__ == ‘__main__‘: #获取当前的模块对象,并获取该模块对象对应的数值. module_name = sys.modules[__name__] print(module_name.__dict__)
visit.py执行的内容:
#!/usr/bin/python # -*- coding:utf-8 -*- import account if hasattr(account,‘Person‘): student = getattr(account,‘Person‘)(‘angela‘,25) print(student.__dict__) if hasattr(student,‘eat‘): func = getattr(student,‘eat‘) func(student)
运行结果:
{‘age‘: 25, ‘name‘: ‘angela‘} angela is eating Process finished with exit code 0
反射的应用场景:
在程序当中,如果我们想通过一个字符串变量var来导入一个模块或者一个模块下的某个方法,这个时候直接执行import var是会报错
的,因为var在程序当中是一个变量,通过字符串变量来直接调用名字看起来相同的函数是不可行的,这个时候就需要使用到反射。
根据用户输入的url的不同,调用不同的函数,实现不同的操作,也就是一个web url路由器的功能,这在web框架里是核心部件之一。
示例程序1:首先,有一个commons模块,它里面有几个函数,分别用于展示不同的页面,代码如下:
#!/usr/bin/python # -*- coding:utf-8 -*- def login(): print(‘这是一个登陆页面!‘) def logout(): print(‘这是一个退出页面!‘) def home(): print(‘这是网站的主页面.‘)
随后,有一个visit模块,通过这个模块可以登录到不同的页面(简易版程序),如果没有使用到反射,大部分人可能会这样写:
#!/usr/bin/python # -*- coding:utf-8 -*- import commons def run(): inp = input(‘请输入你想访问的页面的url:‘).strip() if inp == ‘login‘: commons.login() elif inp == ‘logout‘: commons.logout() elif inp == ‘home‘: commons.home() else: print(‘404‘) if __name__ == ‘__main__‘: run()
运行结果示例:
请输入你想访问的页面的url:login 这是一个登陆页面! Process finished with exit code 0
如果你会使用反射的话,我们就可以这样写(万物皆对象)
import commons def run(): inp = input(‘请输入你想访问的页面的url:‘).strip() if hasattr(commons,inp): func = getattr(commons,inp) func() else: print(‘404‘) if __name__ == ‘__main__‘: run()
示例程序2:我们在模拟一个Ftp的例子,其实道理都是一样的,在Python当中,万物皆对象,只要是对象,就可以使用反射。
#!/usr/bin/python # -*- coding:utf-8 -*- class Ftp_Client(object): def __init__(self,host): self.host = host print(‘正在连接机器:‘,host,‘....‘) def run(self): while True: line = input(‘请输入你需要操作的命令:‘).strip() cmd = line.split()[0] file_name = line.split()[1] if hasattr(self,cmd): func = getattr(self,cmd) print(func) func(file_name) else: print(‘您输入的指令不存在.‘) def get(self,filename): print(‘正在下载文件%s,稍等...‘%filename) ftp_client = Ftp_Client(‘127.0.0.1‘) ftp_client.run()
运行结果:
正在连接机器: 127.0.0.1 .... 请输入你需要操作的命令:get word.txt <bound method Ftp_Client.get of <__main__.Ftp_Client object at 0x00000000024F9A20>> 正在下载文件word.txt,稍等... 请输入你需要操作的命令:put word.txt 您输入的指令不存在. 请输入你需要操作的命令:
示例程序3:反射机制还经常用到协同开发过程当中。
假设现在有一个人A:正在开发一个接口,授权接口:grant。
#!/usr/bin/python # -*- coding:utf-8 -*- class Ugdap(object): def __init__(self,db_name,table_name): self.db_name = db_name self.table_name = table_name # def grant(self): # """ # :return: 该接口正在开发中... # """ # print(‘正在进行授权.‘)
作为调用接口的我,不确认接口是否已经开发完毕,于是我可以这么写:
#!/usr/bin/python # -*- coding:utf-8 -*- from commons import Ugdap Ugdap_obj = Ugdap(‘fdm‘,‘exe_cool_data_operate‘) if hasattr(Ugdap_obj,‘grant‘): func = getattr(Ugdap_obj,‘grant‘) print(func) func() else: print(‘无法获取到接口信息,跳过该步骤.‘)
反射的意义:
可能有人会问python不是有两个内置函数exec和eval吗?他们同样能够执行字符串。比如:
1 2 3 4 5 |
|
那么直接使用它们不行吗?非要那么费劲地使用getattr,__import__干嘛?
其实,在上面的例子中,围绕的核心主题是如何利用字符串驱动不同的事件,比如导入模块、调用函数等等,这些都是python的反射机
制,是一种编程方法、设计模式的体现,凝聚了高内聚、松耦合的编程思想,不能简单的用执行字符串来代替。当然,exec和eval也有它
的舞台,在web框架里也经常被使用。
原文地址:https://www.cnblogs.com/zmyting/p/9515136.html