1.引入
首先我们来用一个例子来引出什么是装饰器以及为什么要使用它. 下面是定义的一个Python function,仅仅是print一行字段
def f1():
print("This is a function 1")
那么此时有了新的需求,需要在函数被调用执行时输出当时的时间,着很简单只需要调用一下其他类库就可以,代码如下
from datetime import datetime
def f1():
print(datetime.now())
print("This is a function 1")
ok, 此时需要确实是达到了,但是假如我们除了f1()还有f2()、f3()…等等方法都需要增加这个需求呢?难道需要给每个方法都去加上这一条输出时间语句?显然这种解决方案是不合适的. 并且它违背了对修改封闭,对扩展开放的原则
因此, 为了解决有多个函数都需要输出被调用时间的需求,并且满足开闭原则, 我们可以使用Python 函数式的特性 专门编写一个函数用来打印时间.
from datetime import datetime
def f1():
print("This is a function 1")
def f2():
print("This is a function 2")
def print_current_time(func):
print(datetime.now)
func
print_current_time(f1())
print_current_time(f2())
上面的代码可以更好实现我们的需求, 这样做逻辑上是没问题的,但是我们调用的时候不再是调用真正的业务逻辑 f1,f2 函数,而是换成了 print_current_time 函数,这就破坏了原有的代码结构, 现在我们不得不每次都要把原来的那个 f 函数作为参数传递给 print_current_time 函数,那么有没有更好的方式的呢?当然有,答案就是装饰器。
2.装饰器
1.简单装饰器
首先来看看什么是简单装饰器
import time
def decorator(func):
def wrapper():
print(time.time())
func()
return wrapper
def f():
print("This is a function")
f = decorator(f)
f()
decorator 就是一个装饰器,它一个普通的函数,它把执行真正业务逻辑的函数 f 包裹在其中,看起来像 f 被 decorator 装饰了一样,decorator 返回的也是一个函数,这个函数的名字叫 wrapper。 这个例子乍一看跟上一步使用的print_current_time方法没有太大区别,甚至没有print_current_time方法简单明了,别急再往下看看@语法糖
2.@语法糖
@ 符号是装饰器的语法糖,它放在函数开始定义的地方,这样就可以省略最后一步再次赋值的操作。
上面的例子我们可以进行一些改变,如下
import time
def decorator(func):
def wrapper():
print(time.time())
func()
return wrapper
@decorator
def f():
print("This is a function")
f()
如上所示,有了 @ ,我们就可以省去f = decorator(f) 这一句了,直接调用 f()即可得到想要的结果。你们看到了没有,f() 函数不需要做任何修改,只需在定义的地方加上装饰器,调用的时候还是和以前一样,如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。
3.*args、**kwargs
可能有人问,如果我的函数 f 需要参数怎么办?很简单只需要同样的给装饰器中wrapper方法加上参数即可 比如:
import time
def decorator(func):
def wrapper(func_name):
print(time.time())
func(func_name)
return wrapper
@decorator
def f(func_name):
print("This function name is" + func_name)
f('first')
同理,如果f方法接收的是多参数只需要将wrapper方法参数变为:*agrs即可
def wrapper(*args):
print(time.time())
func(*args)
同样的,f方法如果还可以接收关键字参数,也只需将wrapper方法参数加上:**kw即可
def wrapper(*args, **kw):
print(time.time())
func(*args, **kw)
4.@wraps
当我们使用了装饰器后,方法名会发现改变
@decorator
def f(func_name):
print(f.__name__)
输出结果是:wrapper,说明此时f的方法名已经变成wrapper,如果想要让使用装饰器的方法名不改变,只需在wrapper方法上加上@wraps
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper():
func()
return wrapper
@decorator
def f():
print(f.__name__)
f()
其实仔细想想,为什么使用装饰器后方法名称会改变? 因为本来最后执行的方法就是wrapper,对比着简单装饰器,我们其实只是用@语法糖看起来是执行的f方法而已。
5.优化装饰器写法
|