• 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
  • 相关阅读:
    Binary Tree Maximum Path Sum
    ZigZag Conversion
    Longest Common Prefix
    Reverse Linked List II
    Populating Next Right Pointers in Each Node
    Populating Next Right Pointers in Each Node II
    Rotate List
    Path Sum II
    [Leetcode]-- Gray Code
    Subsets II
  • 原文地址:https://www.cnblogs.com/wangm-0824/p/10199586.html
Copyright © 2020-2023  润新知