Attention:为了解释得通俗易懂,所以很多概念没有描述的很准确,很多只是“意思意思”,所以大家重在意会哈。如果各位知友有更好的解释和讲解的方法,还请在评论区不吝赐教。
学习python有一段时间了,对于装饰器的理解又多了一些,现在我重新再写一次对于装饰器的理解。
在讲之前我需要先铺垫一下基础知识,如果你已经掌握了就请跳过。
--------------------------------------基础知识分割线-----------------------------------------------------
1、万物皆对象。
在python中,不管什么东西都是对象。对象是什么东西呢?
对象就是你可以用来随意使用的模型。当你需要的时候就拿一个,不需要就让它放在那,垃圾回收机制会自动将你抛弃掉的对象回收。
可能对这个理解有一点云里雾里的感觉,甚至还觉得对这个概念很陌生。其实如果你都学到装饰器这里了,你已经使用过不少对象啦。
比如,我写了一个函数:
defcal(x,y):result=x+yreturnresult
这时,你可以说,你创造了一个叫做cal的函数对象。
然后,你这样使用了它:
cal(1,2)
或者,你这样使用了它:
calculate = calcalculate(1,2)
在第一种方式下,你直接使用了cal这个函数对象;
在第二种方式下,你把一个名为calculate的变量指向了cal这个函数对象。如果各位对类的使用很熟悉的话,可以把这个过程看作“实例化”。
也就是说,对象,就像是一个模子,当你需要的时候,就用它倒一个模型出来,每一个模型可以有自己不同的名字。在上面的例子中,calculate是一个模型,而你写的cal函数就是一个模子。
2、请理解函数带括号和不带括号时分别代表什么意思。
在上一个例子中,如果你只是写一个cal(也就是没有括号),那么此时的cal仅仅是代表一个函数对象;当你这样写cal(1, 2)时,就是在告诉编译器“执行cal这个函数”。
3、请确保能够理解带星号的参数是什么意思。
这个属于函数基础,要是你还没有听说过,那么就该回去好好复习一下了。具体讲解我就略过了。
----------------------------------------------正文分割线---------------------------------------------------
1、装饰器是什么?
装饰器,顾名思义,就是用来“装饰”的。
它长这个样:
@xxx
其中"xxx"是你的装饰器的名字。
它能装饰的东西有:函数、类
2、为什么我需要装饰器?
有一句名言说的好(其实是我自己说的):
“每一个轮子都有自己的用处”
所以,每一个装饰器也有自己的用处。
装饰器主要用来“偷懒”(轮子亦是如此)。
比如:
你写了很多个简单的函数,你想知道在运行的时候是哪些函数在执行,并且你又觉得这个没有必要写测试,只是想要很简单的在执行完毕之前给它打印上一句“Start”,那该怎么办呢?你可以这样:
def func_name(arg): print 'Start func_name' sentences
这样做没有错,but, 你想过没有,难道你真的就想给每一个函数后面都加上那么一句吗?等你都运行一遍确定没有问题了,再回来一个一个的删掉print不觉得麻烦吗?什么?你觉得写一个还是不麻烦的,那你有十个需要添加的函数呢?二十个?三十个?(请自行将次数加到超过你的忍耐阈值)……
如果你知道了装饰器,情况就开始渐渐变得好一些了,你知道可以这样写了:
deflog(func):defwrapper(*arg,**kw):print'Start %s'%funcreturnfunc(*arg,**kw)returnwrapper@logdeffunc_a(arg):pass@logdeffunc_b(arg):pass@logdeffunc_c(arg):pass
其中,log函数是装饰器。
把装饰器写好了之后,只需要把需要装饰的函数前面都加上@log就可以了。在这个例子中,我们一次性就给三个函数加上了print语句。
可以看出,装饰器在这里为我们节省了代码量,并且在你的函数不需要装饰的时候直接把@log去掉就可以了,只需要用编辑器全局查找然后删除即可,快捷又方便,不需要自己手工的去寻找和删除print的语句在哪一行。
-----------------------------------------------重点分割线--------------------------------------------------
3、装饰器原理
在上一段中,或许你已经注意到了"log函数是装饰器"这句话。没错,装饰器是函数。
接下来,我将带大家探索一下,装饰器是怎么被造出来的,来直观的感受一下装饰器的原理。
先回到刚才的那个添加'Start'问题。
假设你此时还不知道装饰器。
将会以Solution的方式呈现。
S1 我有比在函数中直接添加print语句更好的解决方案!
于是你这样做了:
defa():passdefb():passdefc():passdefmain():print'Start a'a()print'Start b'b()print'Start c'c()
感觉这样做好像没什么错,并且还避免了修改原来的函数,如果要手工删改print语句的话也更方便了。嗯,有点进步了,很不错。
S2 我觉得刚刚那个代码太丑了,还可以再优化一下!
于是你这样写了:
defa():passdefb():passdefc():passdefdecorator(func):print'Start %s'%funcfunc()defmain():decorator(a)decorator(b)decorator(c)
你现在写了一个函数来代替你为每一个函数写上print语句,好像又节省了不少时间。你欣喜的喝了一口coffee,对自己又一次做出了进步感到很满意。
嗯,确实是这样。
于是你选择出去上了个厕所,把刚刚憋的尿全部都排空(或许还有你敲代码时喝的coffee)。
回来之后,顿时感觉神清气爽!你定了定神,看了看自己刚才的“成果”,似乎又感到有一些不满意了。
因为你想到了会出现这样的情况:
def main(): decorator(a) m = decorator(b) n = decorator(c) + m for i in decorator(d): i = i + n ......
来,就说你看到满篇的decorator你晕不晕!大声说出来!
S3 你又想了一个更好的办法。
于是你这样写了:
defa():passdefb():passdefc():passdefdecorator(func):print'Start %s'%funcreturnfunca=decorator(a)b=decorator(b)c=decorator(c)defmain():a()b()c()
这下总算是把名字给弄回来了,这样就不会晕了。你的嘴角又一次露出了欣慰的笑容(内心OS:哈哈哈,爷果然很6!)。于是你的手习惯性的端起在桌上的coffee,满意的抿了一口。
coffee的香味萦绕在唇齿之间,你满意的看着屏幕上的代码,突然!脑中仿佛划过一道闪电!要是a、b、c 三个函数带参数我该怎么办?!
你放下coffee,手托着下巴开始思考了起来,眉头紧锁。
像这样写肯定不行:
a = decorator(a(arg))
此时的本应该在decorator中做为一个参数对象的a加上了括号,也就是说,a在括号中被执行了!你只是想要a以函数对象的形式存在,乖乖的跑到decorator中当参数就好了。执行它并不是你的本意。
那该怎么办呢?
你扶了扶眼镜,嘴里开始念念有词“万物皆对象,万物皆对象……”
你的额头上开始渐渐的渗出汗珠。
突然,你的身后的背景暗了下来,一道光反射在眼镜上!不自觉的说了句
“真相はひとつだけ”!
S4 你飞速的写下如下代码。
defa(arg):passdefb(arg):passdefc(arg):passdefdecorator(func):defwrapper(*arg,**kw)print'Start %s'%funcreturnfunc(*arg,**kw)returnwrappera=decorator(a)b=decorator(b)c=decorator(c)defmain():a(arg)b(arg)c(arg)
decorator函数返回的是wrapper, wrapper是一个函数对象。而a = decorator(a)就相当于是把 a 指向了 wrapper, 由于wrapper可以有参数,于是变量 a 也可以有参数了!
终于!你从焦灼中解脱了出来!
不过, 有了前几次的经验,你这一次没有笑。
你又仔细想了想,能不能将a = decorator(a)这个过程给自动化呢?
于是你的手又开始在键盘上飞快的敲打,一会儿过后,你终于完成了你的“作品”。
你在python中添加了一个语法规则,取名为“@”,曰之“装饰器”。
你此时感觉有些累了, 起身打开门, 慢步走出去,深吸一口气,感觉阳光格外新鲜。
你的脸上终于露出了一个大大的笑容。
-------------------------------乱侃结束分割线------------------------------------------------------------
讲到这里,我想大家应该差不多都明白了装饰器的原理。
在评论中有知友问到,要是我的装饰器中也有参数该怎么办呢?
要是看懂了刚才添加参数的解决方案,也就不觉得难了。
再加一层就解决了。
def decorator(arg_of_decorator):
def log(func):
de fwrapper(*arg,**kw):
print 'Start %s'%func
#TODO Add here sentences which use arg_of_decorator
return func(*arg,**kw)
return wrapper
return log