注册 登录

清河洛

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)

这种多个装饰器同时作用与一个函数时,装饰器按照从上到下依次执行



网址导航