装饰器就像礼品盒,对函数进行包装,添加额外的元素(比如一条丝带 ^_^)

装饰的英文是decorate,它的名词就是装饰器decorator。这次我们要介绍的就是这个在python中的叫做decorator的东西,装饰器(起到装饰作用的东西)。

基本的装饰器分为3部分。
1.装饰器本体
2.语法糖(@)
3.被装饰函数

先来一个基本的装饰器瞅瞅:

# python3

# 装饰器本体
def decorator(func):
    def wrapper():
        print("---这里添加装饰功能(比如一条丝带^_^)---")
        return func()
    return wrapper

@decorator  # 语法糖
def test():  
    # 被装饰函数
    print("---被装饰前的函数---")

# test = decorator(test)

if __name__ == "__main__":
    test()

・第4行开始的函数decorator和内部嵌套的函数wrapper组成了是装饰器,是一个闭包构造。

・第10行的@decorator是语法糖,等价于test = decorator(test),是其简写。如果不用@decorator的写法,也可以在test函数后边插入test = decorator(test)以达到同样效果。 另外@后边跟着的decorator是装饰器本体的名字,也是可变的。

・紧跟着@decorator之下的第11行函数test是将要被装饰的函数。

运行效果如下:

---这里添加装饰功能(比如一条丝带^_^)---
---被装饰前的函数---

疑问1:为什么要用闭包来写装饰器?
解答:为了达到在装饰前后可以使用相同语法(比如上文的test() )调用函数test的目的。这样做的好处是你作为编程人员为函数添加了新功能,但并不影响用户端的使用方式。


疑问2:装饰器的闭包构造好难懂,有没有别的方法实现装饰效果?
解答:如果只是达到相同的装饰效果,也可以不使用装饰器:

def wrapper(func):
    print("---这里添加装饰功能(一条丝带^_^)---")
    return func()

def test():
    print("---被装饰前的函数---")

if __name__ == "__main__":
    wrapper(test)  

运行效果相同:

---这里添加装饰功能(比如一条丝带^_^)---
---被装饰前的函数---

和使用装饰器时的不同之处在于函数的执行方法从test()变为了wrapper(test)。
虽然能达到同样的装饰效果,但用户调用命令发生了改变,这样很不好。


总结:装饰器使用闭包构造的目的就在于传参。在使用装饰器时,外层函数decorator把参数(func)传入内层函数wrapper里,内层函数wrapper本身不需要再设置形参。这样当语法糖把函数wrapper作为返回值传递给test时,就可以达到test == wrapper的效果。从而达到用test()的形式来调用被装饰函数的目的