最近在学习 Python 进阶的内容。整理一下关于装饰器(decorator)的一些知识。在解释装饰器前,先总结一些关于函数的知识点。
一切皆对象
在 Python 中,所有的函数都是对象。如下面的例子中,函数可以直接赋值给变量。即使删除了原来的函数,但是变量还是保持了原函数的性质。
1 | def hi(name): |
函数嵌套
在一个函数中,再内嵌一个函数,就能完成函数嵌套。如下所示:
1 | def hi(name='caoqi95'): |
函数返回函数
有些时候,不需要在一个函数里嵌套一个函数,我们也可以将其作为输出返回出来。如下面的代码所示,greet()
函数和 welcome()
函数都可以在一定条件下,被 hi() 函数返回。
1 | def hi(name='caoqi95'): |
上面展示了 a 指向 hi() 函数中的 greet() 函数,现在看看下面的结果:
1 | print(a()) |
为什么会出现不一样的结果呢?因为当函数名后面存在一对小括号的时候,函数才会被执行;而如果没有这对小括号,那该函数就可以被到处传递,并且还可以赋值给别的变量而不去执行它。使用 hi()()
也会与上面一样的结果。
函数作为参数
函数还能作为参数,传给另一个函数使用。如下代码所示,hi()
函数被传递给 doSomeThingBeforeHi(func)
函数使用。
1 | def hi(): |
搭建第一个装饰器
前面回顾了函数的一些基本知识,现在开始学习装饰器的内容。在上一个例子中,其实我们已经创建了一个装饰器。现在稍微修改一下:
1 | def a_new_decorator(a_func): |
在第二步的时候,把 a_function_requiring_decoration
函数当作一个参数赋值给了 a_new_decorator()
函数。然后第三步的时候再添加上一对小括号来执行。
下面用 @ 形式,写成更专业一点的装饰器函数:
1 | def a_new_decorator(a_func): |
看上去很完美,第一个装饰器完成了。但是,上面的装饰器仍然存在一个问题。下面打印函数的名字就能够知道问题出在哪儿了:
1 | print(a_function_requiring_decoration.__name__) |
查看执行的结果,就会发现打印出的名字并不是原来的名字,而是被 warpTheFunction
给取代了。不用怕,Python 这么万能,肯定会有解决方案的。方案如下:
1 | from functools import wraps |
现在问题解决了,总结一下一个装饰器的模板:
1 | from functools import wraps |
最后,再总结一下关于装饰器的内容。其实装饰器实现的就是将函数作为参数提供给其他的函数调用,但是使用装饰器的话,会让代码看上去整洁简短,使代码符合 Python 的核心价值观。装饰器还可以带有参数,还可以作为一个类使用,非常的方便。