前言
面试被问到解释一下装饰器的问题时, 我都会回答到, “装饰器就是一个输入函数返回函数的函数(可调用对象)”. 当然这也是大家经常看到的解释. 直到遇到一个面试官接着问了一句, “装饰器一定要返回函数吗?”, 本文也就是从这个问题开始的.
装饰器是什么
我们可以查阅官方文档装饰器 | 函数定义 | 类定义, 可以发现装饰器大概也就是我前言里的一句话, “装饰器就是一个输入函数返回函数的函数”, 类也可以用装饰器, 无非就是换个词, “一个输入类返回类的函数”.
我在文档中注意到这样一个解释
装饰器语法只是一种语法糖,以下两个函数定义在语义上完全等价:
def f(arg): ... f = staticmethod(f) @staticmethod def f(arg): ...
这也许就是装饰器在语义上的一个本质, 仅仅是把函数f当作参数给函数d, 然后将函数d的返回值重新绑定到名字f上. 所以我猜测装饰器函数可以返回除了函数和类之外的其他对象, 赶紧试试下面的代码.
def d(func):
return 1
@d
def foo():
pass
print(foo, type(foo))
# 1 <class 'int'>
结果不出所料, 装饰器可以返回其他任何类型的对象, 只要你想.
但正如我听到这个问题时, 说出来的第一句话一样, “不返回函数的装饰器还叫装饰器吗?”.
重新认识装饰器
从这里我觉得装饰器这个概念需要从两个方面去讨论
应用
应用上的装饰器就是一种接受一个函数, 返回一个函数的函数(类装饰器自行带入), 广泛应用与面向切面编程的场景, 例如日志输出 | 权限认证 | 错误处理等.
语法与语义
语法上, 装饰器就是一个语法糖, 装饰器函数可以不返回函数, 可以返回任何东西, 最后会重新绑定到变量名上, 当然这个装饰器函数至少要接受一个参数, 不然会报错.
后记
后来我才意识到, 面试官只是想引导告诉我还有类装饰器而已, 并没有到探讨语法与应用一致性的问题, 后面我从这个问题入手重新梳理了一下装饰器, 有了今天这篇文章.
网上没有找到相关的讨论, 这是我自己通过观察代码运行结果和思考的结果, 分成应用和语法两方面也是我主观上分的, 或许有问题, 所以
如有问题, 欢迎交流!