装饰器就是一个闭包,它的主要作用是在不改变原函数的基础上对原函数功能进行扩展。
我们先来写一个简单的函数:
from time import sleep def foo(): print("Hello World!") sleep(2)
现在我们要对这个函数加一些功能,比如说打印这个函数的执行时间
有人说,这还不简单啊,直接修改foo函数呢,确实,这是最简单的一种方法。但是在我们实际工作中,有时候是不能对调用的函数进行修改的,那该怎么办呢?
我们可以重新写个打印时间的函数去调用foo函数啊,比如:
import time from time import sleep def foo(): print("Hello World!") sleep(2) def show_time(func): start_time = time.time() func() end_time = time.time() print(end_time - start_time)
调用:
show_time(foo) # 输出: Hello World! 2.0000178813934326
很好,确实是在不修改foo函数的基础上实现了需求,但是。。。。。有个大问题,改变了函数的调用方式,前面是调用foo()的,现在需要修改成调用show_time()。
看来这种方法也不行。
这个时候就是闭包大展身手的机会了
import time from time import sleep def foo(): print("Hello World!") sleep(2) def show_time(func): def inner(): start_time = time.time() func() end_time = time.time() print(end_time - start_time) return inner # 调用 foo = show_time(foo) foo() # 输出 Hello World! 2.000027656555176
到这里已经完美地实现了我们的需求,这就是装饰器。装饰器有个高大上的写法,就是装饰器符号@
上面的代码我们修改成用装饰器符号@来写:
import time from time import sleep def show_time(func): def inner(): start_time = time.time() func() end_time = time.time() print(end_time - start_time) return inner @show_time # 其实就相当于foo = show_time(foo) def foo(): print("Hello World!") sleep(2) # 调用 foo()
@show_time的作用就是执行foo()时,跳转到去执行inner(),如下图:
被装饰函数的参数
如果被装饰的函数有形参,那么在装饰器函数中该怎么写呢?
import time from time import sleep def show_time(func): def inner(*x): start_time = time.time() func(*x) end_time = time.time() print(end_time - start_time) return inner @show_time def add(*args): sum = 0 for arg in args: sum += arg print(sum) sleep(2) add(3, 5)
装饰器函数参数
有些时候我们可能需要对不同的被装饰函数加些不同的功能,比如需要对某些函数加上日志打印,那该怎么做呢?
import time from time import sleep def logger(flag="false"): def show_time(func): def inner(*x): start_time = time.time() func(*x) end_time = time.time() print(end_time - start_time) if flag == "true": print("打印日志记录") return inner return show_time @logger("true") # 打印日志记录 def add(*args): sum = 0 for arg in args: sum += arg print(sum) sleep(2) @logger() # 不需要打印日志记录 def foo(): print("Hello World!") sleep(2)