深入理解python装饰器
1、装饰器是什么?
装饰器,顾名思义,就是用来“装饰”的。
它长这个样:
@xxx
其中”xxx”是你的装饰器的名字。
它能装饰的东西有:函数、类
2、为什么我需要装饰器?
装饰器主要用来“偷懒”
比如:
你写了很多个简单的函数,你想知道在运行的时候是哪些函数在执行,并且你又觉得这个没有必要写测试,只是想要很简单的在执行完毕之前给它打印上一句“Start”,那该怎么办呢?你可以这样:
def func_name(arg):
print 'Start func_name'
sentences
这样做没有错,but, 你想过没有,难道你真的就想给每一个函数后面都加上那么一句吗?等你都运行一遍确定没有问题了,再回来一个一个的删掉print不觉得麻烦吗?什么?你觉得写一个还是不麻烦的,那你有十个需要添加的函数呢?二十个?三十个?(请自行将次数加到超过你的忍耐阈值)……
如果你知道了装饰器,情况就开始渐渐变得好一些了,你知道可以这样写了:
def log(func):
def wrapper(*arg, **kw):
print 'Start %s' % func
return func(*arg, **kw)
return wrapper
@log
def func_a(arg):
pass
@log
def func_b(arg):
pass
@log
def func_c(arg):
pass
其中,log函数是装饰器。
把装饰器写好了之后,只需要把需要装饰的函数前面都加上@log就可以了。在这个例子中,我们一次性就给三个函数加上了print语句。
可以看出,装饰器在这里为我们节省了代码量,并且在你的函数不需要装饰的时候直接把@log去掉就可以了,只需要用编辑器全局查找然后删除即可,快捷又方便,不需要自己手工的去寻找和删除print的语句在哪一行。
3、装饰器原理
在上一段中,或许你已经注意到了”log函数是装饰器”这句话。没错,装饰器是函数。
接下来,我将带大家探索一下,装饰器是怎么被造出来的,来直观的感受一下装饰器的原理。
先回到刚才的那个添加’Start’问题。
假设你此时还不知道装饰器。
将会以Solution的方式呈现。
S1 我有比在函数中直接添加print语句更好的解决方案!
于是你这样做了:
def a():
pass
def b():
pass
def c():
pass
def main():
print 'Start a'
a()
print 'Start b'
b()
print 'Start c'
c()
感觉这样做好像没什么错,并且还避免了修改原来的函数,如果要手工删改print语句的话也更方便了。嗯,有点进步了,很不错。
S2 我觉得刚刚那个代码太丑了,还可以再优化一下!
于是你这样写了:
def a():
pass
def b():
pass
def c():
pass
def decorator(func):
print 'Start %s'% func
func()
def main():
decorator(a)
decorator(b)
decorator(c)
你现在写了一个函数来代替你为每一个函数写上print语句,好像又节省了不少时间。
但是会出现这样的情况:
def main():
decorator(a)
m = decorator(b)
n = decorator(c) + m
for i in decorator(d):
i = i + n
......
来,就说你看到满篇的decorator你晕不晕!大声说出来!
S3 你又想了一个更好的办法。
于是你这样写了:
def a():
pass
def b():
pass
def c():
pass
def decorator(func):
print 'Start %s' % func
return func
a = decorator(a)
b = decorator(b)
c = decorator(c)
def main():
a()
b()
c()
这下总算是把名字给弄回来了,这样就不会晕了。要是a、b、c 三个函数带参数我该怎么办?!
像这样写肯定不行:
a = decorator(a(arg))
此时的本应该在decorator中做为一个参数对象的a加上了括号,也就是说,a在括号中被执行了!你只是想要a以函数对象的形式存在,乖乖的跑到decorator中当参数就好了。执行它并不是你的本意。
那该怎么办呢?
S4 你飞速的写下如下代码。
def a(arg):
pass
def b(arg):
pass
def c(arg):
pass
def decorator(func):
def wrapper(*arg, **kw)
print 'Start %s' % func
return func(*arg, **kw)
return wrapper
a = decorator(a)
b = decorator(b)
c = decorator(c)
def main():
a(arg)
b(arg)
c(arg)
decorator函数返回的是wrapper, wrapper是一个函数对象。而a = decorator(a)就相当于是把 a 指向了 wrapper, 由于wrapper可以有参数,于是变量 a 也可以有参数了!
还没有评论,来说两句吧...