zoukankan      html  css  js  c++  java
  • 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那样新增逻辑或者修改对应的逻辑,易于维护。到此反射就简单介绍到这里。

  • 相关阅读:
    03 Python之变量以及常量介绍
    看女程序员是如何处理男友出轨,网友回复更精彩
    四面美团,收割 offer
    MySQL 优化实战记录
    龙岗一个月350的出租房,我搬出来了
    程序员工作 996 生病 ICU ?
    真的有人在偷听我们讲话么?
    一次非常有趣的 SQL 优化经历
    如何阅读Java源码?
    从 0 开始手写一个 Mybatis 框架,三步搞定!
  • 原文地址:https://www.cnblogs.com/caesar-id/p/12864876.html
Copyright © 2011-2022 走看看