zoukankan      html  css  js  c++  java
  • python装饰器

    开闭原则:
    在不修改原函数及其调用方式的情况下对原函数功能进行扩展
    对代码的修改是封闭
    不能修改被装饰的函数的源代码
    不能修改被装饰的函数的调用方式

    用函数的方式设想一下游戏里用枪的场景

    def game():
        print('压子弹')
        print('枪上膛')
        print('发射子弹')
    game()
    game()
    game()
    
    此时需要给枪增加一个瞄准镜,比如狙击远程目标时候需要加,狙击近程目标不用加
    此时上边的代码就变成了现在的代码
    
    def sight():
        print('专业狙击瞄准镜')
        game()
    sight()
    sight()
    sight()
    此时的设计就不符合开闭原则(因为修改了原代码及调用名称)

    装饰器(python里面的动态代理)
    本质: 是一个闭包
    组成: 函数+实参高阶函数+返回值高阶函数+嵌套函数+语法糖 = 装饰器
    存在的意义: 在不破坏原有函数和原有函数调用的基础上,给函数添加新的功能

    通用装饰器写法:

    def warpper(fn):        # fn是目标函数相当于func
        def inner(*args,**kwargs):      # 为目标函数的传参
            '''在执行目标函数之前操作'''
            ret = fn(*args,**kwargs)    # 调用目标函数,ret是目标函数的返回值
            '''在执行目标函数之后操作'''
            return ret      # 把目标函数返回值返回,保证函数正常的结束
        return inner
    
    #语法糖
    @warpper    #相当于func = warpper(func)
    def func():
        pass
    func()      #此时就是执行的inner函数

    上边的场景用装饰器修改后

    方式一
    def game():
        print('压子弹')
        print('枪上膛')
        print('发射子弹')
    
    def sight(fn):      # fn接收的是一个函数
        def inner():
            print('安装专业狙击瞄准镜')
            fn()        #调用传递进来的函数
            print('跑路')
        return inner    #返回函数地址
    
    game = sight(game)  #传递game函数到sight函数中
    game()
    
    执行步骤
    第一步定义两个函数game()为普通函数,sight()为装饰器函数
    第二步定义game = sight(game)等于把game函数当做参数传递给sight(fn)装饰器函数fn形参
    第三步执行sight(fn),fn在形参位置,相当于下边函数game()传参过来等于fn
    第四步执行inner函数,然后return把inner函数内存地址当做返回值返回给sight(game)
    第五步然后执行game(),相当于执行inner函数
    第六步,执行inner函数,打印'狙击镜',执行fn()形参,由于fn形参等于game函数,所以执行game()函数,打印'压子弹','上膛','发射子弹'
    第七步打印'跑路'
    第八步把打印的结果返回给game()
    
    方式二
    def sight(fn):      # fn接收的是一个函数
        def inner():
            print('安装专业狙击瞄准镜')
            fn()        #调用传递进来的函数
            print('跑路')
        return inner    #返回函数地址
    
    @sight      #相当于game = sight(game)
    def game():
        print('压子弹')
        print('枪上膛')
        print('发射子弹')
    game()
    
    执行步骤
    第一步执行sight(fn)函数
    第二步执行@sight,相当于把把game函数与sight装饰器做关联
    第三步把game函数当做参数传递给sight(fn)装饰器函数fn形参
    第四步执行inner函数,然后return把inner函数内存地址当做返回值返回给@sight
    第五步执行game()相当相当于执行inner()函数,因为@sight相当于game = sight(game)
    第六步打印'瞄准镜
    第七步执行fn函数,因为fn等于game函数,所以会执行game()函数,打印'压子弹','上膛','发射子弹'.fn()函数执行完毕
    第八步打印'跑路'
    第九步然后把所有打印的结果返回给game()
    
    结果
    安装专业狙击瞄准镜
    压子弹
    枪上膛
    发射子弹
    跑路

    一个简单的装饰器实现

    def warpper(fn):
        def inner():
            print('每次执行被装饰函数之前都要先经过这里')
            fn()
        return inner
    @warpper
    def func():
        print('执行了func函数')
    func()
    
    结果
    每次执行被装饰函数之前都要先经过这里
    执行了func函数

    带有一个或多个参数的装饰器

    def sight(fn):                      #fn等于调用game函数
        def inner(*args,**kwargs):      #接受到的是元组("bob",123)
            print('开始游戏')
            fn(*args,**kwargs)    #接受到的所有参数,打散传递给user,pwd
            print('跑路')
        return inner
    @sight
    def game(user,pwd):
        print('登陆游戏用户名密码:',user,pwd)
        print('压子弹')
        print('枪上膛')
        print('发射子弹')
    game('bob','123')
    结果
    开始游戏
    登陆游戏用户名密码: bob 123
    压子弹
    枪上膛
    发射子弹
    跑路

    动态传递一个或多个参数给装饰器

    def sight(fn):                      #调用game函数
        def inner(*args,**kwargs):      #接受到的是元组("bob",123)
            print('开始游戏')
            fn(*args,**kwargs)    #接受到的所有参数,打散传递给正常的参数
            print('跑路')
        return inner
    @sight
    def game(user,pwd):
        print('登陆游戏用户名密码:',user,pwd)
        print('压子弹')
        print('枪上膛')
        print('发射子弹')
        return '游戏展示完毕'
    ret = game('bob','123')     #传递了两个参数给装饰器sight
    print(ret)
    
    @sight
    def car(qq):
        print('登陆QQ号%s'%qq)
        print('开始战车游戏')
    ret2 = car(110110)          #传递了一个参数给装饰器sight
    print(ret2)
    结果
    开始游戏
    登陆游戏用户名密码: bob 123
    压子弹
    枪上膛
    发射子弹
    跑路
    None
    开始游戏
    登陆QQ号110110
    开始战车游戏
    跑路
    None
    你会发现这两个函数执行的返回值都为None,但是我game定义返回值了return '游戏展示完毕',却没给返回

    装饰器的返回值

    为什么我定义了返回值,但是返回值还是None呢,是因为我即使在game函数中定义了return '游戏展示完毕'
    但是装饰器里只有一个return inner定义返回值,但是这个返回值是返回的inner函数的内存地址的,并不是inner
    函数内部的return所以默认为None,所以应该定义一个inner函数内部的return返回值,而且也没有接收返回值的变量,
    所以要要设置ret = fn(*args,**kwargs)和return ret
    
    def sight(fn):                      #调用game函数
        def inner(*args,**kwargs):      #接受到的是元组("bob",123)
            print('开始游戏')
            ret = fn(*args,**kwargs)    #接受到的所有参数,打散传递给正常的参数
            print('跑路')
            return ret
        return inner
    @sight
    def game(user,pwd):
        print('登陆游戏用户名密码:',user,pwd)
        print('压子弹')
        print('枪上膛')
        print('发射子弹')
        return '游戏展示完毕'
    ret = game('bob','123')     #传递了两个参数给装饰器sight
    print(ret)
    结果
    开始游戏
    登陆游戏用户名密码: bob 123
    压子弹
    枪上膛
    发射子弹
    跑路
    游戏展示完毕
    
    
    事例2
    def wrapper_out(flag):      #装饰器本身的参数
        def wrapper(fn):        #目标函数
            def inner(*args,**kwargs):  #目标函数需要接受的参数
                if flag == True:
                    print('找第三方问问价格行情')
                    ret = fn(*args,**kwargs)
                    print('买到装备')
                    return ret
                else:
                    ret = fn(*args,**kwargs)
                    return ret
            return inner
        return wrapper
    #语法糖,@装饰器
    @wrapper_out(True)
    def func(a,b):  #被wrapper装饰
        print(a,b)
        print('开黑')
        return 'func返回值'
    abc = func('我是参数1','我是参数2')
    print(abc)
    结果
    找第三方问问价格行情
    我是参数1 我是参数2
    开黑
    买到装备
    func返回值

    多个装饰器同用一个函数

    def wrapper1(fn):
        def inner(*args,**kwargs):
            print('wrapper1-1')
            ret = fn(*args,**kwargs)
            print('wrapper1-2')
            return ret
        return inner
    
    def wrapper2(fn):
        def inner(*args,**kwargs):
            print('wrapper2-1')
            ret = fn(*args,**kwargs)
            print('wrapper2-2')
            return ret
        return inner
    
    def wrapper3(fn):
        def inner(*args,**kwargs):
            print('wrapper3-1')
            ret = fn(*args,**kwargs)
            print('wrapper3-2')
            return ret
        return inner
    @wrapper1
    @wrapper2
    @wrapper3
    def func():
        print('我是测试小白')
    func()
    结果
    wrapper1-1
    wrapper2-1
    wrapper3-1
    我是测试小白
    wrapper3-2
    wrapper2-2
    wrapper1-2
  • 相关阅读:
    html中label及加上属性for之后的用法
    Django中利用filter与simple_tag为前端自定义函数的实现方法
    关于自动编译iOS工程,生成app及ipa文件的方法-备
    ios打包ipa的四种实用方法(.app转.ipa)-备
    为GCD队列绑定NSObject类型上下文数据-利用__bridge_retained(transfer)转移内存管理权-备
    GCD使用经验与技巧浅谈--备
    h5与iOS的wkwebview不兼容问题
    cocoaPods 安装和应用
    .a静态库的注意事项
    UIApplication详解再解-备
  • 原文地址:https://www.cnblogs.com/wangm-0824/p/10199586.html
Copyright © 2011-2022 走看看