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

 

posted @ 2018-12-30 15:30  清茶i  阅读(152)  评论(1编辑  收藏  举报