python中的装饰器
qingheluo2021-08-11清河洛335
在Python中,万物皆对象,因此函数也不例外。就是说函数是一种特殊类型的变量,可以和其余变量一样,作为参数传递给函数,也可以作为返回值返回。一、函数的装饰器装饰器的需求:def demo():
print(‘hello world‘)
现在需要打印出demo函数执行时的时间,我们修改为:
def demo():
print("当前时间:", datetime.datetime.now())
print(‘hello world‘)
如果有几百个函数同时需要实现该功能,那么就需要一个一个修改,好不容易修改完了领导又提出新的需求了,显然一个一个修改并不现实,于是...
在Python中,万物皆对象,因此函数也不例外。就是说函数是一种特殊类型的变量,可以和其余变量一样,作为参数传递给函数,也可以作为返回值返回。
一、函数的装饰器
装饰器的需求:
def demo(): print(‘hello world‘) 现在需要打印出demo函数执行时的时间,我们修改为: def demo(): print("当前时间:", datetime.datetime.now()) print(‘hello world‘) 如果有几百个函数同时需要实现该功能,那么就需要一个一个修改,好不容易修改完了领导又提出新的需求了,显然一个一个修改并不现实,于是我们想到了一个办法: def run(func): print("当前时间:", datetime.datetime.now()) func() 通过run(demo)来实现功能的扩展,后期也只需要修改run函数就可以新增加功能。 但是这样的话,我们每次都要传递一个函数给run函数,如果原来执行业务逻辑是只要调用demo函数,那么这种做法破坏了原有的代码逻辑结构,那有没有更好的方式,当然,那就是装饰器。
所谓的装饰器
其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改
装饰器是使用@符号引用已有的函数后,可用于修饰其他函数装饰被修饰的函数。
当程序使用“@函数A”装饰另一个函数B时:
将被修饰的函数B作为参数传给@符号引用的函数A
将函数B替换(装饰)成第1步的返回值
最后,被“@函数”修饰的函数不再是原来的函数,而是被替换换成一个新函数。
def decorator(func): def wrapper(): print("当前时间:", datetime.datetime.now()) func() return wrapper @decorator def demo(): print(‘hello world‘) demo() @符号是装饰器的语法糖,在定义函数的时候使用,可以避免再一次赋值操作 此处@decorator相当于 demo=decorator(demo) demo() 先是执行demo=decorator(demo),此时变量demo指向的是decorator()。 decorator(func)中传参是hello,返回wrapper,而wrapper又会调用到原函数demo()。 上述代码里的decorator()就是一个装饰器,它“增强”了demo()的功能,但是并没有去真正的改变hello()函数的内部实现
实际场景中,很多情况下需要向被装饰的函数中传入参数
def decorator(func): def wrapper(*args,**kwargs): print("当前时间:", datetime.datetime.now()) func(*args,**kwargs) return wrapper @decorator def demo(name): print(‘hello %s‘ % name) demo(‘qingheluo‘)
还有一种情况就是装饰器函数也可能会需要传入参数
def my_decorator(num): def decorator(func): def wrapper(*args,**kwargs): print("当前时间:", datetime.datetime.now()) for i in range(num): func(*args,**kwargs) return wrapper return decorator @my_decorator(5) def demo(name): print(‘hello %s‘ % name) demo(‘qingheluo‘)//demo(‘qingheluo‘)会执行5次
另外,有些场景我们会需要使用函数的元数据,如func.__name__
def decorator(func): def wrapper(*args,**kwargs): print("当前时间:", datetime.datetime.now()) func(*args,**kwargs) return wrapper @decorator def demo(name): print(‘hello %s‘ % name) print(demo.__name__) 会打印wrapper,而我们的预期是demo 这说明,它不再是以前的那个demo()函数,而是被wrapper()函数取代了,函数名等函数属性会发生改变,当然这对结果不会产生什么影响,但是这并不符合我们的预期。
这时候可以用内置装饰器@functools.wrap来实现(需要import functools)
def decorator(func): @functools.wraps(func) def wrapper(*args,**kwargs): print("当前时间:", datetime.datetime.now()) func(*args,**kwargs) return wrapper @decorator def demo(name): print(‘hello %s‘ % name) print(demo.__name__) 会打印demo,符合我们的预期
二、类装饰器
装饰器可以通过函数来实现,我们也可以使用类来实现一个装饰器。
类能实现装饰器的功能,是由于当我们调用一个类的实例时,就调用到它的__call__()方法
class MyDecorator: def __init__(self,func): self.func=func def __call))(self, *args, **kwargs): print("当前时间:", datetime.datetime.now()) return self.func(*args,**kwargs) @MyDecorator def demo(name): print(‘hello %s‘ % name)
三、多层装饰器
如果我们需要在一个函数上使用多个装饰器该怎么办呢,其实很简单,就是每行一个装饰器添加到指定函数就可以了
@decorator1 @decorator2 @decorator3 def hello(name): print(‘hello %s‘ % name)
这种多个装饰器同时作用与一个函数时,装饰器按照从上到下依次执行