Python 装饰器的用法 一篇细讲(初学角度入手)

2021.10.15

1.前话

  1. 因为我也是初学装饰器,所以可以较白话地讲它。
  2. 为什么突然学python装饰器?

发现代码可以更简洁,减少重复代码和重复工作,便于维护。

  1. 具体场景:

在写网站的时候,许多视图函数需要做是否登录判断或者是否允许操作判断,这样每个视图函数前面都要重复一遍代码,到后期,维护是个大问题:需要找到所有函数,一个个修改。

2.Python 装饰器

  1. 一个类似的场景,比如:我希望在下面这个函数的前面输出before,后面输出after。
def func():
    print('我是func函数')
    value = (1,2,3,)
    return value

res = func()
print(res)
  1. 如果只是一两个函数,我们完全可以这样
def func():
    print('before')
    print('我是func函数')
    print('after')
   	value = (1,2,3,)
    return value

func()
  1. 但如果是几十个几百个函数,哪天告诉你,我想改成前面after,后面before,或者说我不这么做了,就得在这几十个几百个函数中,改啊改。
  2. 但如果使用装饰器,就减少了大量的重复工作和工作量。
  3. 装饰器原理

outer函数return inner,也就是说func = outer(func),即func=inner。

当执行func()的时候,即执行inner()。

inner里执行origin(),在inner里找不到origin,往上一层找,即outer,找到origin,origin即前面传过来的参数func,所以依然还是执行func(),执行origin()即执行func()。func函数有返回值,return res。

def outer(origin):
    def inner():
    	res = origin()
    	return res
    return inner

def func():
    print('我是func函数')
    value = (1,2,3,)
    return value

func = outer(func)

res = func()
print(res)
  1. 就上面的问题,也是在函数前和函数后加输出。
def outer(origin):
    def inner():
        print('before')
        res = origin()
        print('after')
        return res
    return inner

执行结果如下:
在这里插入图片描述
是不是感觉结果一样,还更麻烦了?
这就是前面说的,如果量少可以直接在函数里,前后加输出。
量大再用装饰器。

  1. 上面是原理,接下来才是python中装饰器的真正用法

@装饰器函数名
def 函数():

再func上一行加@outer 相当于 func=outer(func)
(注意:装饰器的函数要放在使用装饰器的函数之前。)

@outer
def func():
    print('我是func函数')
    value = (1,2,3,)
    return value

res = func()
print(res)
  1. 如果现在有三个函数需要在前面和后面加输出,我只需要这些函数前,加上一个@outer,就完成了,而不是在每个函数里加两个print。可以想象一下不止三个,而是一百个函数。(当然是在有统一需求的时候更适用。)
def outer(origin):
    def inner():
        print('before')
        res = origin()
        print('after')
        return res
    return inner

@outer
def func():
    print('我是func函数')
    value = (1,2,3,)
    return value

@outer
def func2():
    print('我是func2函数')
    value = (1,2,3,)
    return value

@outer
def func3():
    print('我是func3函数')
    value = (1,2,3,)
    return value

res = func()
res1 = func2()
res2 = func3()

输入结果:
在这里插入图片描述

  1. 带n个参数的装饰器

上面讲的都是不带传参的函数,接下来讲带参数的。
假如,现在func有一个参数,func2有两个,func3有一个或者多个。

如果只在func函数中加入参数。
那么会报参数错误。
在这里插入图片描述
因为现在不仅仅是func函数的问题了,func = outer(func),现在的func = inner(),现在写的inner()没有接收参数,所以得补上,为了能接收不定个数的参数,我们用*args,**kwargs接收。

def outer(origin):
    def inner(*args, **kwargs):
        print('before')
        res = origin(*args, **kwargs)
        print('after')
        return res
    return inner

inner加了接收参数,但是最终还是执行的origin(),即func(),参数也不能少。
(这边inner是接收参数,origin是传参数)

3.总结

在这里插入图片描述

4.补充:扩展

def outer(origin):
    def inner(*args, **kwargs):
        '''inner文档注释'''
        print('before')
        res = origin(*args, **kwargs)
        print('after')
        return res
    return inner


@outer
def func(a1):
    '''func的文档注释'''
    print('我是func函数')
    value = (1,2,3,)
    return value

@outer
def func2(a1,a2):
    '''func2的文档注释'''
    print('我是func2函数')
    value = (1,2,3,)
    return value

print(func.__name__)
print(func.__doc__)
print(func2.__name__)
print(func2.__doc__)

__name__是获取函数名
__doc__是获取函数文档注释

输出结果:
在这里插入图片描述
都是inner的函数名和文档注释。
如果希望是原来函数的函数名和注释的话,需要引入functools
在inner函数前加个@functools.wrap(传过来的函数名)。

import functools

def outer(origin):
    @functools.wraps(origin)  # inner.__name__=origin.__name__
    def inner(*args, **kwargs):
        '''inner文档注释'''
        print('before')
        res = origin(*args, **kwargs)
        print('after')
        return res
    return inner

输出结果:
在这里插入图片描述

5.再用装饰器有感(更新一下:2022/11/7)

def outer(origin):
    def inner(x):
        for i in range(x):
            origin(i)
    return inner


@outer
def func(i):
    print(i)

func(5)

运行结果如下:
在这里插入图片描述
参数:
在这里插入图片描述
函数:
在这里插入图片描述
调用func函数,就是调用inner,所以inner接收的参数即调用时的传参;
而func函数接收的参数即inner内调用origin时的传参。origin即func函数。
基本套路

def outer(origin):
    def inner():
		origin()
    return inner


@outer
def func():
	pass

理解:
可以把装饰器函数想象成一个框架,可以往里面嵌入不同的功能,这个嵌入的功能即被装饰的函数。像龙骑的腰带,是通用的,往里面放入不同的卡盒,就是不同的骑士了。而腰带就是装饰器,卡盒是被装饰的函数。
例如:占位符,string = f’this is {xxx}’ ,string就是装饰器,{xxx}占位符就是被装饰的函数。
实际应用:例如每个函数前面都需要进行一个对消息队列的读取操作,就可以把读取操作写在装饰器inner内,然后将具体业务逻辑函数放在读取之后的操作,将读取的结果当作参数传给业务逻辑函数。

希望对初学装饰器的你有帮助。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值