Python的@装饰器是干什么用的?

25 阅读5分钟

这个嘛,直接暴力。但是呢,这里只有4个函数,假如你开发的计算器有几十几百个函数,每个函数都要套上if语句,这不得麻烦死了,不烦死也啰嗦死了

所以怎么弄简单一点呢?聪明的你肯定想到了,我们可以把那个判断if也单独定义一个函数,然后把计算用的函数套在里面,就像这样:

def check(a,b,func): #定义检查函数,变量为待检测参数a,b和检测通过后执行的函数func

if type(a)==type(0) and type(b)==type(0):

return func(a,b)

else:

print('Type must be number')

return None

def plus(a,b):

return a+b

def minus(a,b):

return a-b

...

#主程序

check(1,2,plus) #计算1+2

check(1,2,minus) #计算1-2

check(1,2,multiply) #计算1*2

check(1,2,divide) #计算1/2

这里面有一点一定要特别注意,主程序的check(1,2,plus) 是把plus函数本身作为变量传递给check,由check函数决定如何执行plus函数,此处不能写成check(1,2,plus(1,2)),plus不能带参数和括号,不是执行plus()后把结果传给check。

这么写程序简洁了不少,加减乘除函数只需要定义他们本身的运算就可以了,变量检测交给了check函数。这么写也是比较容易理解的。

但是对于使用该程序的用户来说,就不是这么回事了,他们会觉得这么写非常难看

为什么呢?我是要拿程序做加减乘除计算的,但我不论计算什么,每次都是在主调用check这个函数

那有没有什么办法,可以既好看,又简洁呢?装饰器就是起到了这个神奇的作用。

上面这个需求,用装饰器可以这么写:

def check(func):

...

@check

def plus(a,b):

return a+b

@check

def minus(a,b):

return a-b

...

#主程序

plus(1,2) #计算1+2

minus(1,2) #计算1-2

...

先直观感受一下,通过@check,check函数就被“注入”到了plus函数中,使得plus函数拥有了参数检测的功能。这样,在主程序中,若要计算加法就可直接调用plus,便可先校验再计算。

那么,这个装饰器check要怎么定义呢?我们来看一下。

def check(func): #定义装饰器check

def newfunc(a,b): #定义函数模板,即如何处理func

if type(a)==type(0) and type(b)==type(0):

return func(a,b)

else:

print('Type must be number!')

return None

return newfunc #将处理后的func作为新函数newfunc输出

@check

def plus(a,b):

return a+b

#主程序,计算1+2

plus(1,2)

我们可以看到,当装饰器@check作用于plus函数时,plus函数本身作为参数func传入装饰器中。在装饰器check的定义内部,定义了一个函数模板,描述了对输入的func如何处理。可以看到,newfunc对func(也就是输入的plus)套用了判断数据类型的if语句,最后,再将套好的newfunc输出,替代原来的func。这样,此时执行func就是在执行newfunc,执行plus就是在执行套上if语句的新函数。

所以,通过装饰器,添加上了判断语句的新函数替换了原来的plus函数,但仍通过plus这个函数名调用,所以看起来就是plus函数被“装饰”了。

当然了,如果大家在网络上搜索,关于如何定义装饰器,看到的是一个更加规范的版本。看起来更难理解一些,但其实是一样的:

def checkall(func):

def wrapper(*args,**kwargs):

if type(args[0])==type(0) and type(args[1])==type(0):

return func(*args,**kwargs)

else:

print('Type must be number!')

return None

return wrapper

模板函数一般习惯用wrapper来表示,这个没啥,建议大家都这么写,规范一些。

参数一般用不定长的*args,**kwargs来表示,这个可能有些人就困惑了。因为被装饰的函数可能有很多种,参数的个数一般也不确定。然后*args,**kwargs是什么东西?args,kwargs这两个形参英文字母是什么无所谓可以自己定,关键是前面的单星号*和双星号**。

假如我定义一个函数,不能确定参数有多少个,例如要对输入的一组数字做连加操作。那么就可以定义plus(*x),当调用该函数时,若输入多个变量plus(1,2,3),那么就会把输入的变量组合成一个元祖x=(1,2,3)输入。定义双星号plus(**x)的意思是,调用该函数时若写出形参变量plus(a=1,b=2,c=3),那么输入变量就会组合成字典x={a:1,b:2,c:3}传入函数。

当然也可以反向操作,定义函数的时候参数个数是明确的plus(a,b,c),那么调用该函数时,加上星号plus(*(1,2,3)),就是对输入元祖(1,2,3)执行炸开操作,转换为plus(1,2,3)输入。

最后

🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。

了解详情:docs.qq.com/doc/DSnl3ZG…