装饰器
装饰器是干什么用的?
装饰器可以在不修改某个函数的情况下,给函数添加功能。
形象点来说,从前有一个王叔叔,他一个人住在家里,每天打扫家,看书。于是定义如下一个函数:
def uncle_wang():
sweeping()
reading()
后来呢,有一天,大头儿子一家搬到了王叔叔隔壁 :-D 。根据剧情,一天,大头儿子的妈妈请王叔叔来家里吃饭,那么,王叔叔的日程就添加了“去隔壁吃饭”这一项,但是又不能修改王叔叔之前的日程,怎么实现?这时,就可以给王叔叔添加一个装饰器,给这个装饰器起个名字,姑且就叫neighbor
,然后就写成这样:
@neighbor
def uncle_wang():
sweeping()
reading()
然后王叔叔去大头儿子家吃饭就提上日程啦,哈哈。(只是吃饭哦,不要想多了(⊙v⊙))
这个neighbor
其实也是个函数,参数就是uncle_wang
(没错,可以把函数名当成参数传来传去,还能当做返回值),在装饰器里面实现“去隔壁吃饭”:
def neighbor(func):
eat_next_door() # 自定义函数,去隔壁吃饭
return func # 原来的函数不变,直接返回
最后是执行,直接运行uncle_wang()
即可
# 执行函数
uncle_wang() # 相当于不加装饰器,直接执行 neighbor(uncle_wang)()
这感觉就像是用neighbor
装饰了uncle_wang
,丰富了王叔叔的生活,从此变成了隔壁老王。实现方式就是套娃,给uncle_wang
套个neighbor
,变成neighbor(uncle_wang)
(这整个东西是个函数名),然后调用这个函数: neighbor(uncle_wang)()
,形如:函数名()
在Python里,这个套娃的操作简化成了装饰器,直接在原函数上面添加@neighbor
,然后调用的时候还是写成uncle_wang()
,但是这个装饰过的王叔叔已经不是原来的王叔叔了,他现在其实是隔壁老王。
王叔叔的新日程搞定了,但是还有个问题,就是顺序。现在的日程顺序相当于:
eat_next_door()
sweeping()
reading()
请人吃饭当然是吃晚饭啦,所以eat_next_door()
需要排在最后面,而neighbor
函数不能先返回(return func
)然后才执行eat_next_door()
,众所周知,函数返回了就结束了,后面的东西都不管了。
所以,继续套娃,再搞个函数进去,写成这样:
def neighbor(func):
def wrapper(): # 套娃函数,注意这里是定义,不是执行
func() # 相当于不带装饰器的 uncle_wang()
eat_next_door() # 自定义函数,去隔壁吃饭
return wrapper # 直接返回套娃函数
这样顺序就对了,王叔叔很满意~
现在这个装饰器基本成型了,但是现在还不能处理原函数的参数和装饰器函数的参数,继续改进的实现方式可以去看廖雪峰老师的教程,写得很不错,我就是从那学来的。
附上完整代码:
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
def sweeping():
print('sweeping')
def reading():
print('reading')
def eat_next_door():
print('eat_next_door')
def neighbor(func):
def wrapper(): # 套娃函数,注意这里是定义,不是执行
func() # 相当于不带装饰器的 uncle_wang()
eat_next_door() # 自定义函数,去隔壁吃饭
return wrapper # 直接返回套娃函数
@neighbor
def uncle_wang():
sweeping()
reading()
if __name__ == "__main__":
uncle_wang()
(嗨,又水了一篇,之前还说要测一下手动实现和库函数实现的二分查找的耗时差距,正事还是放到下次吧……)
参考资料:
- 装饰器 - 廖雪峰的官方网站
- 隔壁老王的梗是怎么来的 - 知乎