Python装饰器(一)
这段时间学习Python,之前已经学习过基本的语法,现在准备重新拾起来,看的教程是github上面的Python - 100天从新手到大师。进度相对一般的教程比较快,虽然没有那么详细,但是能够快速了解整体的框架。
第九天的教程里面讲到了装饰器,这个之前了解过,但是一直似懂非懂,加上之前用的比较少,也是看过就算了。这次又出现了,不能再糊弄过去,要认真理解透彻。这里把个人的理解过程记录下来。
参考博客
这里先放出来我查看过的资料,如果你看过以后懂了,那么恭喜,下面我写的内容就不用看了。
简单的函数装饰器
装饰器在Python中很强大,扩展函数功能而不改变函数的代码,同时能够多次复用。在查看完上面的资料后我个人还有几点没有弄清楚,后来慢慢有了自己的一些理解,记录如下。
函数是变量
一定要先理解在Python语言中,一切皆对象,函数也能当变量。那么赋值,作为另一个函数参数这些都不在话下。
1 | def foo(): # 最简单的函数 |
看完赋值,接着再看一个例子,函数作为另一个函数的参数
1 | def foo(): |
上面的函数都很简单,没有返回值,而且函数内部也没有再次定义函数,再看一个函数内部定义函数,然后并返回的例子。
1 | def bar(): |
上面的例子就是把函数作为返回值返回,这时候返回的函数就会赋值给b,当执行b()
的时候即执行了foo()
。
装饰器
看到这里就可以谈谈装饰器了,装饰器的作用个人感觉就是在要执行的函数外面再加上一层函数来实现一些非核心的功能,这样能够避免破坏原函数的结构,同时还能实现增加功能的需求。
以上面博客中常用的打印日志的代码为例:
1 | def log(func): |
这样做是可以的,不过缺点上面的博客也提到了,这样做就等于调用了log
函数,跟原来的函数foo
没啥关系了。同时还有一点注意就是这里直接执行log(foo)
即可得到结果,log
函数没有返回值,所以也没有赋值给其他的变量。
装饰器的效果
装饰器的语法就不多说了,而装饰器的效果是装饰器接收真正的业务函数然后把返回的内容再次赋值给业务函数名,以此来增强业务函数的功能。了解了这个闪腰的操作你就不会纠结为啥装饰器要那么费劲再返回函数。上例子
1 | def log1(func): |
wrapper函数为什么返回func()
其实到这里基本概念就差不多了,但是我在查看教程的时候发现wrapper函数都是直接返回func(),这里一直不明白,直接调用就好了啊,为什么要返回呢。后面我测试了一些例子,发现高手还是高手。
1 | def log(func): |
当然如果再想的多点,为什么wrapper返回的是func()
,不是func
。而log返回的则是函数名wrapper
。
如果没有wrapper函数会怎样
这个问题我也没想到,在知乎的问答中看到的地址如下:点我,我把问题复现如下:
1 | def log(func): |
分析一下:其实如果你此时正在郁闷,说明你也被欺骗了,请在第一个foo()
前增加一行print('***')
,真相就会出现。因为在装饰器运行的时候执行如下命令foo=log(foo)
这个时候就会输出前2行内容。然后log(foo)
返回的func
赋值给了foo
,所以foo
根本没变,执行后就输出一行内容this is foo function。
其实上面的这个例子也解答了,为什么那些教程中的例子把函数的代码内容都放在wrapper
函数中,而没有放在最外层的log
中。
带参数的装饰器
到这里,基本函数装饰器的最简单的使用应该理解差不多了,后面就是在以上基础上再增加功能,如被装饰的函数有参数怎么办,装饰器需要参数怎么办,多个装饰器如何工作。
1 | def log(level): |
这里的装饰器的作用是foo = log(5)(foo)
,则foo = decorator(foo) = warpper
,这时再调用foo('aoenian')
实则为warpper('aoenian')
。
这次先分析这么多,还有一些用法后续再补充,如果有更好的理解方法或思路,欢迎留言探讨。