• python中的反射


    什么是反射?

    反省、自省。

    反射指的是一个对象具有检测、访问、修改自身属性的能力

    反射本质上就是在使用四个内置函数,然后对属性进行增啥改查。

    class A:
        x = 1
        def f(self):
            print('我是函数f')
    
    a = A()
    

    hasattr(object, '属性名'): 查看对象object中有没有’属性名‘这个属性。因为类也是一个对象,所以第一个参数也可以直接是类名。一切皆对象:所有对象都可以反射。返回值:bool

    
    bool_value = hasattr(a, 'x')
    print(bool_value)  #True
    
    bool_value = hasattr(A, 'x')
    print(bool_value)  #True
    

    getattr(objcet, '属性名', '没有这个属性时的默认返回值'):

    相当于:obj.属性

    参数:第三个参数,指的是object中没有指定的字符串属性时,会默认将第三个参数返回。

    返回值:返回对象里面的一个属性

    x = getattr(a, 'x')
    print(x)  # 1
    
    f = getattr(a, 'f')
    print(f)  # <bound method A.f of <__main__.A object at 0x10da19b10>>
    f()  # 我是函数f
    

    setattr(object, '属性名', '属性值'):

    相当于:obj.属性 = 值

    第一个参数:反射的对象

    第二个参数:反射的对象的属性名

    第三个参数:要修改的反射的对象的值

    返回值:None

    setattr(A, 'x', '设置后的值')
    print(A.x) # 设置后的值
    print(a.x) #设置后的值
    

    delattr(object, '要删除的属性'):

    返回值None

    delattr(A, 'x')
    print(A.x) 
    #    print(A.x)
    # AttributeError: type object 'A' has no attribute 'x'
    

    反射的使用场景

    1、对属性进行增删改查,使用对象 __ dict _ 也可以对属性进行增删改查,但是语法繁琐,不好理解。

    2、如果这个对象不是自己写的,就必须判断这个对象是否满足,也就是是否是我需要的属性。

    if hasattr(a, 'f'):
        func = getattr(a, 'f')
        func()
    else:
        print('f这个函数不存在时要处理的逻辑')
        
    # 确认A类里面的有没有f这个函数名,, 这时候调用
    	
    
    from module import A
    
    # 在你的代码中,需要用到别人的代码,但是别人出去玩了代码没写完。这个时候就可以利用反射只定义接口,后期再完善。
    
    a = A()
    if hasattr(a, 'test'):
        #得到a的test方法
        test = getattr(a, 'test')
    
    else:
        print('不存在此方法,执行其他的逻辑')
    
    #反射当前模块
    import sys
    
    def s1():
        print('s1')
    x
    def s2():
        print('s2')
    
    
    this_module = sys.modules[__name__]
    
    hasattr(this_module, 's1')
    s = getattr(this_module, 's2')
    s()
    
    #动态导入
    import importlib
    
    # 拿到模块,根据模块的路径
    plugin = importlib.import_module('lib.plugins')
    print(plugin)
    # 从模块中取出类
    cls = getattr(plugin, 'WinCMD')
    # obj = cls()
    # obj.cd()
    
    s = setattr(cls, 'x', 'cdd')
    print(s)
    print(cls.x)
    
    

    反射机制

    #写一个函数
    def func():
        print('我是一个函数')
    #写一个字符串
    s = 'func'
    

    我们可以通过 func+()的方式调用func这个函数,但是我们却不能用字符串 'func'()的形式调用func函数。说白了就是:不能通过字符串来调用名字看起来相同的函数!

    反射机制要做的事就是:让字符串可以直接调用名字相同的函数!

    func = getattr(object, s)是关键,通过getattr函数,从object里面查找名字跟s相同的函数名(这里s = 'func'),然后将函数返回赋值给func变量,这时候func+()就能直接调用object里面的函数。

    getattr做的事,其实就是把一个字符串转为一个函数的过程。这里就是把字符串’func‘ 转为了函数 func

    实现网站url路由功能

    #网站的视图 views.py 文件
    
    def login():
        print('我是登录页面')
    
    
    def logout():
        print('我是退出')
    
    
    def index():
        print('我是网站首页')
    
    
    #运行文件 start.py
    import views
    
    def run():
        while True:
            '''
            url = 'www.baidu.com/index'
            这个url指向的视图为index
            '''
            url = input('输入要跳转的路径:').strip()
            view = url.split('/')[-1]
    
            if view == 'index':
                views.index()
    
            if view == 'login':
                views.login()
    
            if view == 'logout':
                views.logout()
    

    上面模拟了一个简单的路由系统,可以看到,url的最后一个 / 后面的内容,是views.py 视图文件里面的一个方法。上面有三个视图,所以写了三个路由的if语句。试想一下,如果一个网站有几百上千的url,是不是要写上千的 if语句? 显然:这个不科学的。

    由于 url 的最后一个 / 后面的字符串,跟视图文件里面的函数名相同,我们可不可以直接把字符串当做函数来用?这个时候,反射机制就可以大显身手了。

    对执行文件稍作修改:

    #运行文件 start.py
    import views
    
    def run():
        while True:
            '''
            url = 'www.baidu.com/index'
            这个url指向的视图为index
            '''
            url = input('输入要跳转的路径:').strip()
            view = url.split('/')[-1]
    
            if hasattr(views, view):
                func = getattr(views, view) # getattr如果对象里面没有与字符串相同属性就会报错,所以需要先hasattr一下,查看字符串对应的属性存不存在。
                func()
            else:
                print('url 404 ')
    
    

    上面的这种情况只针对,运行文件start.py和视图文件views.py在同一个文件夹下的情况。在实际项目中,视图文件都是分散在各个不同目录的不同模块中的。为了解决这个问题,可以使用 __ import __ 解决。

    __ import __ (字符串参数), __ import __ 会根据字符串参数,动态导入同名的模块。

    项目路径示意:

    -- my_app

    --|—user.py

    --|—|—index()

    --|—|—check_info()

    —|—core.py

    --|—|— home() #跳转到 home

    在此修改 start.py 文件的代码:

    #start.py
    def run():
        while True:
            '''
            url = 'www.baidu.com/index'
            这个url指向的视图为index
            '''
            url = input('输入要跳转的路径:').strip()
            modules, view = url.split('/')   #user/index  core/home
    
            views = __import__(modules)  #动态导入同名模块,不用再手动导入了。
    
            if hasattr(views, view):
                func = getattr(views, view)
                func()
            else:
                print('url 404 ')
    

    如果 user.py 和 core.py 在一个 lib 包下面,跟启动文件start.py不在同一个目录下,如下所示:

    my_app

    —lib

    —|—user.py

    —|—|—index() #跳转到用户首页

    —|—|—check_info()

    —|—core.py

    —|—|— home() #跳转到 home

    —|—start.py

    
    def run():
        while True:
            '''
            url = 'www.baidu.com/index'
            这个url指向的视图为index
            '''
            url = input('输入要跳转的路径:').strip()
            modules, view = url.split('/')   #user/index  core/home
    		#自然的会想到在modules前面加个lib
            #看起来似乎没什么问题,和import lib.core的传统方法类似,但实际上运行的时候会有错误。__import__需要加个参数  fromlist=True
            views = __import__('lib.' + modules, fromlist=True)
    
            if hasattr(views, view):
                func = getattr(views, view)
                func()
            else:
                print('url 404 ')
    
    
  • 相关阅读:
    gulp自动化压缩合并、加版本号解决方案
    利用gulp 插件gulp.spritesmith 完成小图合成精灵图,并自动输出样式文件
    gulp插件(8)
    gulp插件 run-sequence(同步执行任务)
    gulp合并压缩
    Freemarker 基础概念
    querystring模块详解
    深入理解JavaScript的闭包特性如何给循环中的对象添加事件
    堆中的路径
    公式求值
  • 原文地址:https://www.cnblogs.com/KbMan/p/11279348.html
Copyright © 2020-2023  润新知