最近面网易互娱的时候,面试官看到简历上写了Python,于是问我Python装饰器会不会。Ummmm好像还真不会,最后面试官说我Python没有掌握得很好。想来也是,自己使用Python无非就是用来写各种算法题和各种调库,很少去学Python本身的一些东西(比方说修饰器,多线程,变量作用域等等)。讲道理Python在这么多编程语言里面应该算是很好学的了,感觉还是该花时间好好学一下,让它变成自己真正掌握的东西。
那我就先从装饰器开始。
为什么需要装饰器
这里用一个很经典的例子。假设我有一个简单的函数:
1 | def f(a, b): |
现在我们想要统计函数的运算时间,那么我们正常的思路是对函数进行修改,加上一个计时器的功能。
1 | import time |
但很多时候我们是不能直接修改核心函数本身的,因为这便混淆了函数原本的功能,失去了封装性。另外,如果我们希望为函数包装上多种不同的功能,比方说多种精度的计时器,那我们需要频繁地修改函数本身,而且不方便测试。
那么我们可以尝试用另一种方法,新定义一个函数,将原函数作为输入,然后在新函数里实现计时器的功能。
1 | import time |
但这也会带来其它的问题。如果你在代码的不同角落都调用了f(a, b),那你需要在这些所有地方都用新的timer替换f。
回过头来想,这个做法实际上就是用一个新的函数来包装原函数本身,然后在调用原函数的基础上增添新的功能,于是便有了装饰器。
装饰器怎么搞
装饰器的用法其实很简单,我们只需要定义一个新的函数,这个函数接受原函数作为参数,然后返回一个与原函数函数签名相同(即参数和返回值相同)的函数,最后用 @deco 关键字进行修饰即可。
1 | def deco(f): |
通过这样的方式,我们便将原函数替换为新的函数(添加了新的功能),并且不需要在代码本身及每个调用函数的地方都进行修改。
更泛化的装饰器
从上面的装饰器我们能看到一个问题,我们这个用作计时器的装饰器实际上功能有限,只能用来装饰带两个参数没有返回值的函数。那如果我们想要用一个计时器装饰器来装饰所有的函数,该怎么做呢?
这里我们可以用到python中的可变参数。
1 | def deco2(f): |
这样,我们就可以通过这个装饰器来修饰几乎所有的函数了。
多重装饰
如果我们想要为原函数添加多个不同的功能(比方说,计时并打印当前线程的id),该怎么办?很简单,因为修饰器是可以嵌套的。
1 | def deco(f): |
输出如下:
1 | Deco 1 |
注意这里嵌套的顺序为:deco(deco2(func))