python中的反射
Posted id_iot
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python中的反射相关的知识,希望对你有一定的参考价值。
python反射简介
所谓反射是指通过字符串的方式获取对象,然后执行对象的属性或方法。在python中一切皆对象,因此我们可以对一切事物进行发射。
关于反射python为我们提供了四个方法:
hasattr(object, name):name必须是字符串,如果字符串name是object对象当中的某一属性或某一方法将返回True,否则返回False。
getattr(object, name ,default=None):name必须是字符串,如果字符串name是object对象当中的某一属性或某一方法将返回对应的属性或方法,否则如果给定默认值将返回默认值,如果没指定默认值将引发AttributeError的异常。
setattr(object, name, value):可以新增或修改对象的属性,name必须是字符串,value可以是任意数据类型。如果name是object的属性将修改对应属性的值为value,如果对象不是object的属性将新增一个属性。
delattr(object, name):删除对象属性,name必须是字符串,name必须是object的属性且object对这个属性有删除的权限,否则将引发AttributeError。
下面以对对象进行反射,对类进行反射,对当前模块进行反射,对其它模块进行反射为示例来了解反射的应用。
对类进行反射
class A: num = 10 def fun(self): print(\'Hello World\') # 判断A中是否有fun属性或方法 if hasattr(A,\'fun\'): # 获取fun方法的内存地址 f = getattr(A,\'fun\') # 执行A中fun的方法 f(\'\') # 打印结果如下 Hello World # 给A中新增属性buf setattr(A,\'buf\',[1,2,3]) print(A.buf) # 打印内容如下 [1, 2, 3] # 删除类中属性num delattr(A,\'num\') print(A.num) # 打印结果如下 AttributeError: type object \'A\' has no attribute \'num\'
对对象进行反射
class A: num = 10 def fun(self): print(\'Hello World\') obj = A() # 判断对象obj中是否有fun方法 if hasattr(obj,\'fun\'): # 获取对象中fun方法的内存地址 f = getattr(obj,\'fun\') # 执行obj中fun方法 f() # 打印结果如下 Hello World # 给对象obj新增属性buf setattr(obj,\'buf\',[1,2,3]) print(obj.buf) # 打印结果如下 [1, 2, 3] # 删除obj中属性buf delattr(obj,\'buf\') # 删除类A的属性num delattr(obj,\'num\') # 引发如下异常 delattr(obj,\'num\') AttributeError: num 原因:obj是类A的实例化对象,它有自己的内存空间,与类A空间互不干涉,obj仅可以访问类A的属性和方法,是不能对类A中原有的属性和方法进行修改的。
对当前模块进行发射
import sys class A: num = 10 def fun(self): print(\'Hello World\') def f(): print(\'对当前模块进行反射\') # 获取当前模块 current_module = sys.modules[__name__] # 获取当前模块下的类A if hasattr(current_module,\'A\'): obj_A = getattr(current_module,\'A\') obj_A.fun(\'\') # 获取当前模块下的函数f if hasattr(current_module,\'f\'): obj_f = getattr(current_module,\'f\') obj_f() # 打印内容如下 Hello World 对当前模块进行反射
对其它模块进行反射
# 1.py对test.py进行反射 ============================ # test.py中代码如下 class A: num = 10 def fun(self): print(\'Hello World\') def f(): print(\'对当前模块进行反射\') ============================ # 1.py中代码如下 # 第一种方式 import test # 获取test.py模块下的类A if hasattr(test,\'A\'): obj_A = getattr(test,\'A\') obj_A.fun(\'\') # 获取test.py模块下的函数f if hasattr(test,\'f\'): obj_f = getattr(test,\'f\') obj_f() # 打印内容如下 Hello World 对当前模块进行反射 # 第二种方式 imp_module = __import__(\'test\') # 获取test.py模块下的类A if hasattr(imp_module,\'A\'): obj_A = getattr(imp_module,\'A\') obj_A.fun(\'\') # 获取test.py模块下的函数f if hasattr(imp_module,\'f\'): obj_f = getattr(imp_module,\'f\') obj_f() # 打印内容如下 Hello World 对当前模块进行反射
关于getattr(object, name ,default=None)中的参数default需要注意,直接按位置参数传参即可,不要是要关键字参数进行传参,否则会报错。
class A: num = 10 def fun(self): print(\'Hello World\') # getattr正确的默认参数 obj = getattr(A,\'max\',\'没找到\') print(obj) # 打印内容如下 没找到 # getattr错误的默认参数 obj = getattr(A,\'max\',default=\'没找到\') # 打印内容如下 TypeError: getattr() takes no keyword arguments
反射的应用
看了上面的代码片段,也许你会感觉反射也不过如此,但是在有些场景反射真的很有用,下面以FTP server和FTP client为例,我之前写的小练习。
有兴趣的朋友可以看一下:https://www.cnblogs.com/caesar-id/p/12105321.html里面服务端对客户端发来的命令进行解析时就是用的反射,如果不用反射,就需要使用大量的if来解析客户端要执行哪条命令。
下面在不使用反射的情况下解析客户端发来的命令(伪代码):
class DataAnalysis(FileOperation): """ 数据分析处理类,主要负责解析client发送过来的指令。 """ def syntax_analysis(self,recv_data, socket_obj, commom): """ 负责解析客户端传来的数据。 :param recv_data:客户端执行的命令 :param socket_obj:socket对象 :param commom:数据对象 :return: """ # 用户要执行的命令可能是help 也可能是get 文件 客户端路径 还可能是cd 目录 # 所以首先要明确用户到底要执行什么用的命令,通过切割后获取第一个元素 # 我们通过第一个元素来判断用户到底要做什么,下面是判断用户到底用做什么 clientData = recv_data.split(" ") if clientData[0] == \'help\': pass elif clientData[0] == \'get\': pass elif clientData[0] == \'cd\': pass elif clientData[0] == \'put\': pass ...
上面的情况很明显的就是程序有多少功能,就需要有多少个if来对其进行判断,当某个方法发生改变或新增功能时,需要到if中添加相应的逻辑判断或者修改对应的逻辑,无论是从代码的阅读还是后期的维护都不是很方便。
下面使用反射的情况下解析客户端发来的命令:
class DataAnalysis(FileOperation): """ 数据分析处理类,主要负责解析client发送过来的指令。 """ def syntax_analysis(self,recv_data, socket_obj, commom): """ 负责解析客户端传来的数据。 :param recv_data:客户端执行的命令 :param socket_obj:socket对象 :param commom:数据对象 :return: """ # 用户要执行的命令可能是help 也可能是get 文件 客户端路径 还可能是cd 目录 # 所以首先要明确用户到底要执行什么用的命令,通过切割后获取第一个元素 # 我们通过第一个元素来判断用户到底要做什么,下面是判断用户到底用做什么 clientData = recv_data.split(" ") if hasattr(self, clientData[0]): # 判断用户执行的命令是否存在 get_fun = getattr(self, clientData[0]) # 获取命令的执行方法 get_fun(clientData, socket_obj, commom) # 执行对应的命令 else: pass
使用反射对客户端命令解析,无论程序有多少方法,都只需上面那几行代码即可。相比通过if对命令解析反射结构更加清晰,当某个方法发生改变,或添加新的功能,只需要把所有精力都用在功能实现上即可,不需要像if那样新增逻辑或者修改对应的逻辑,易于维护。到此反射就简单介绍到这里。
以上是关于python中的反射的主要内容,如果未能解决你的问题,请参考以下文章