python函数装饰器(装饰器类及装饰器函数)的理解

python函数装饰器(装饰器类及装饰器函数)的理解

函数作为函数装饰器

一个简单的“执行后打印”装饰器执行流程分析

如果要实现一个装饰器功能,即先运行某个函数,运行后,将函数的返回值使用print打印出来,用函数作为函数装饰器的实现为:

from functools import wraps
def print_after_execution_decorator_function(func):
	@wraps(func)
	def wrapped_func(*args, **kwargs):
	    ret = func(*args, **kwargs)
	    print(ret)
	    return ret
	return wrapped_func 

对应的使用是:

@print_after_execution_decorator_function
def work_f(i):
	return i+1

当调用work_f函数的时候,输入10,则会在屏幕上打印出11;
分析其中的具体流程如下:

(1)调用work_f,实际上调用的是print_after_execution_decorator_function(work_f)
这里函数装饰器里的@可以大致理解为调用其后面紧接的对象的__call__()方法,参数为下一行中def后面定义的函数,即:print_after_execution_decorator_function.__call__(work_f)

(2)既然如此,来看print_after_execution_decorator_function里实现了什么过程,函数的参数是func,返回值是内部的嵌套函数wrapped_func,那么在逻辑上有:

wrapped_func == print_after_execution_decorator_function.__call__(work_f)

(3)如果带上参数,逻辑上有:

wrapped_func(*args, **kwargs) == print_after_execution_decorator_function.__call__(work_f)(*args, **kwargs)

结合(1)、(3),可以发现,在调用work_f(*args, **kwargs)的时候,实际上调用的是wrapped_func(*args, **kwargs),而wrapped_func内部的实现为执行func函数后,获得函数返回值ret,并打印,然后正常返回到上层;注意在wrapped_func里,func是一个由外层函数传递进来的变量,就是work_f;这么一来,就完成了所说的功能。

@wraps的作用

wrapped_func的前面也有一个装饰器@wraps,这个装饰器的作用就是把被装饰函数的__name__属性改回其本身,具体来看,在这个例子里,如果将@wraps(func)注释掉,并在work_f里增加一行打印函数__name__属性的代码,即:

def print_after_execution_decorator_function(func):
	# @wraps(func)
    def wrapped_func(*args, **kwargs):
        ret = func(*args, **kwargs)
        print(ret)
        return ret
    return wrapped_func

@print_after_execution_decorator_function
def work_f(i):
	print(work_f.__name__)
	return i+1

输入i=10,打印出来有两行内容:

wrapped_func
11

这很容易理解,因为前面说到调用work_f(*args, **kwargs)的时候,实际上调用的是wrapped_func(*args, **kwargs),打印__name__实际也发生在wrapped_func执行的过程中;

而如果取消@wraps(func)的注释,再运行一次,打印出的内容变为:

work_f
11

由此可以看出@wraps装饰器的作用,就是在调用函数的时候,使被装饰函数的__name__属性不被装饰器所影响。

类作为函数装饰器

使用类改写“执行后打印”装饰器

前面说到:@可以大致理解为调用其后面紧接的对象的__call__()方法,参数为下一行中def后面定义的函数,也就是说函数装饰器可以为任何带有__call__()方法的对象,因此,只要在类中定义一个__call__()方法,类可以作为函数装饰器:

from functools import wraps
class PrintAfterExecutionDecoratorClass:
    def __call__(self, func):
        @wraps(func)
        def wrapped_func(*args, **kwargs):
            ret = func(*args, **kwargs)
            print(ret)
            return ret
        return wrapped_func

@PrintAfterExecutionDecoratorClass()
def work_c(i):
    return i+1

在还没有调用work_c函数之前,就已经完成了如下事件:

  • 实例化PrintAfterExecutionDecoratorClass类的一个匿名对象(执行__init__()方法)
  • 执行该匿名对象的__call__()方法,将work_c函数与wrapped_func进行绑定

可以通过在__init__()__call__()中添加print输出来证明这一点;

在调用work_c函数的时候,按照前面叙述的类似的过程,实际调用的是PrintAfterExecutionDecoratorClass类匿名对象的__call__()所返回的wrapped_func函数。

为装饰器添加参数

如果要实现的装饰器功能更为复杂,例如“错误后重试”装饰器,且重试次数需要根据给定的参数来调整,这时就不能使用装饰器函数(因为函数装饰器的__call__()只能使用需要装饰的函数作为唯一输入),而只能使用装饰器类,并将这些参数在__init__()中传入:

from functools import wraps
class RetryIfError:
    def __init__(self, retry_times):
        self.retry_times = retry_times
    def __call__(self, func):
        @wraps(func)
        def wrapped_func(*args, **kwargs):
            for i in range(self.retry_times):
                try:
                    ret = func(*args, **kwargs)
                    return ret
                except Exception as e:
                    print(e)
                    continue
            return None
        return wrapped_func

@RetryIfError(3)
def work_r():
    a = [].pop()
    return a

ret = work_r()
print(ret)

这个函数装饰器在尝试运行被装饰的函数抛出异常后,打印异常信息、重新尝试运行该函数,最多三次,如果仍然失败,则返回None,运行代码后,输出为:

pop from empty list
pop from empty list
pop from empty list
None

如果想更改尝试次数,可以修改@RetryIfError()的参数,例如尝试五次,则写为@RetryIfError(5),输出结果为:

pop from empty list
pop from empty list
pop from empty list
pop from empty list
pop from empty list
None

猜你喜欢

转载自blog.csdn.net/O_1CxH/article/details/123707239