深入理解python装饰器

偏执的太偏执、 2023-06-08 06:17 96阅读 0赞

1、装饰器是什么?

装饰器,顾名思义,就是用来“装饰”的。

它长这个样:

@xxx

其中”xxx”是你的装饰器的名字。

它能装饰的东西有:函数、类

2、为什么我需要装饰器?

装饰器主要用来“偷懒”

比如:

你写了很多个简单的函数,你想知道在运行的时候是哪些函数在执行,并且你又觉得这个没有必要写测试,只是想要很简单的在执行完毕之前给它打印上一句“Start”,那该怎么办呢?你可以这样:

  1. def func_name(arg):
  2. print 'Start func_name'
  3. sentences

这样做没有错,but, 你想过没有,难道你真的就想给每一个函数后面都加上那么一句吗?等你都运行一遍确定没有问题了,再回来一个一个的删掉print不觉得麻烦吗?什么?你觉得写一个还是不麻烦的,那你有十个需要添加的函数呢?二十个?三十个?(请自行将次数加到超过你的忍耐阈值)……

如果你知道了装饰器,情况就开始渐渐变得好一些了,你知道可以这样写了:

  1. def log(func):
  2. def wrapper(*arg, **kw):
  3. print 'Start %s' % func
  4. return func(*arg, **kw)
  5. return wrapper
  6. @log
  7. def func_a(arg):
  8. pass
  9. @log
  10. def func_b(arg):
  11. pass
  12. @log
  13. def func_c(arg):
  14. pass

其中,log函数是装饰器。

把装饰器写好了之后,只需要把需要装饰的函数前面都加上@log就可以了。在这个例子中,我们一次性就给三个函数加上了print语句。

可以看出,装饰器在这里为我们节省了代码量,并且在你的函数不需要装饰的时候直接把@log去掉就可以了,只需要用编辑器全局查找然后删除即可,快捷又方便,不需要自己手工的去寻找和删除print的语句在哪一行。

3、装饰器原理

在上一段中,或许你已经注意到了”log函数是装饰器”这句话。没错,装饰器是函数。

接下来,我将带大家探索一下,装饰器是怎么被造出来的,来直观的感受一下装饰器的原理。

先回到刚才的那个添加’Start’问题。

假设你此时还不知道装饰器。

将会以Solution的方式呈现。

S1 我有比在函数中直接添加print语句更好的解决方案!

于是你这样做了:

  1. def a():
  2. pass
  3. def b():
  4. pass
  5. def c():
  6. pass
  7. def main():
  8. print 'Start a'
  9. a()
  10. print 'Start b'
  11. b()
  12. print 'Start c'
  13. c()

感觉这样做好像没什么错,并且还避免了修改原来的函数,如果要手工删改print语句的话也更方便了。嗯,有点进步了,很不错。

S2 我觉得刚刚那个代码太丑了,还可以再优化一下!

于是你这样写了:

  1. def a():
  2. pass
  3. def b():
  4. pass
  5. def c():
  6. pass
  7. def decorator(func):
  8. print 'Start %s'% func
  9. func()
  10. def main():
  11. decorator(a)
  12. decorator(b)
  13. decorator(c)

你现在写了一个函数来代替你为每一个函数写上print语句,好像又节省了不少时间。

但是会出现这样的情况:

  1. def main():
  2. decorator(a)
  3. m = decorator(b)
  4. n = decorator(c) + m
  5. for i in decorator(d):
  6. i = i + n
  7. ......

来,就说你看到满篇的decorator你晕不晕!大声说出来!

S3 你又想了一个更好的办法。

于是你这样写了:

  1. def a():
  2. pass
  3. def b():
  4. pass
  5. def c():
  6. pass
  7. def decorator(func):
  8. print 'Start %s' % func
  9. return func
  10. a = decorator(a)
  11. b = decorator(b)
  12. c = decorator(c)
  13. def main():
  14. a()
  15. b()
  16. c()

这下总算是把名字给弄回来了,这样就不会晕了。要是a、b、c 三个函数带参数我该怎么办?!

像这样写肯定不行:

a = decorator(a(arg))

此时的本应该在decorator中做为一个参数对象的a加上了括号,也就是说,a在括号中被执行了!你只是想要a以函数对象的形式存在,乖乖的跑到decorator中当参数就好了。执行它并不是你的本意。

那该怎么办呢?

S4 你飞速的写下如下代码。

  1. def a(arg):
  2. pass
  3. def b(arg):
  4. pass
  5. def c(arg):
  6. pass
  7. def decorator(func):
  8. def wrapper(*arg, **kw)
  9. print 'Start %s' % func
  10. return func(*arg, **kw)
  11. return wrapper
  12. a = decorator(a)
  13. b = decorator(b)
  14. c = decorator(c)
  15. def main():
  16. a(arg)
  17. b(arg)
  18. c(arg)

decorator函数返回的是wrapper, wrapper是一个函数对象。而a = decorator(a)就相当于是把 a 指向了 wrapper, 由于wrapper可以有参数,于是变量 a 也可以有参数了!

发表评论

表情:
评论列表 (有 0 条评论,96人围观)

还没有评论,来说两句吧...

相关阅读