装饰器拓展

一:装饰器的缺点,解决方法

正常的函数,可以使用内置方法打印函数名,注释等方便开发。

def f(*args, **kwargs):
    """
    这是一个测试装饰器的函数,没什么其他的用法
    :param args:
    :param kwargs:
    :return:
    """
    print("2018-06-04")

print(f.__name__)
print(f.__doc__)

# f

# 这是一个测试装饰器的函数,没什么其他的用法
# :param args:
# :param kwargs:
# :return:

在加上装饰器之后,使用内置方法打印函数名,变成了inner,注释成了None

def wrapper(func):
    def inner(*args, **kwargs):
        print("start")
        ret = func(*args, **kwargs)
        print("end")
        return ret
    return inner

@wrapper
def f(*args, **kwargs):
    """
    这是一个测试装饰器的函数,没什么其他的用法
    :param args:
    :param kwargs:
    :return:
    """
    print("hello world")

print(f.__name__)
print(f.__doc__)

# inner
# None
View Code

出现上面的情况的原因是因为函数经过装饰之后,实际上都是在执行inner函数。

为了避免这种情况,可以引入一个模块来解决这个问题

from functools import wraps


def wrapper(func):
    @wraps(func)
    def inner(*args, **kwargs):
        print("start")
        ret = func(*args, **kwargs)
        print("end")
        return ret

    return inner


@wrapper
def f(*args, **kwargs):
    """
    这是一个测试装饰器的函数,没什么其他的用法
    :param args:
    :param kwargs:
    :return:
    """
    print("hello world")


print(f.__name__)
print(f.__doc__)

# f
# 
# 这是一个测试装饰器的函数,没什么其他的用法
# :param args:
# :param kwargs:
# :return:
View Code

二:多个装饰器的执行顺序

大多的认知仅是:多个装饰器的执行顺序从上到下,现在,通过一个例子来深入的分析一下装饰器装饰的函数的整个运行过程。

def wrapper_a(func):          #执行结果:返回一个函数a
print(func.__name__+'_wrapper_a')
print('*' * 20)
def a(*args, **kwargs):
print('get inner a')
func(*args, **kwargs)
return a

def wrapper_b(func): #执行结果:返回一个函数b
print(func.__name__+'_wrapper_b')
print('*'*20)
def b(*args, **kwargs):
print('get inner b')
func(*args, **kwargs)
return b

@wrapper_a
@wrapper_b
def f():
print("hello world")

f()
# f_wrapper_b
# ********************
# b_wrapper_a
# ********************
# get inner a
# get inner b
# hello world

在被装饰函数f加括号调用时,函数f作为参数,被传入就近的装饰器wrapper_b中,然后装饰器wrapper_b开始执行。

#执行结果有三:
1:执行:print(func.__name__+'_wrapper_b')和print('*'*20)
    f_wrapper_b
    ********************
2:执行:def b(*args, **kwargs):
       .........
       .........
  定义了一个闭包函数a,此时:func
= f, 3:执行:return b 把以func= f 包裹的闭包函数b作为返回值返回给调用者。 注意:此时函数b是定义阶段,不会执行函数体代码。

在得到返回值后,返回值作为一个参数,被传入装饰器wrapper_a中,然后装饰器wrapper_a开始执行。

注意:此时的返回值是一个闭包函数b,其代码如下:

func = f
def b(*args, **kwargs):
    print('get inner b')
    func(*args, **kwargs)

wrapper_a执行的结果如下:

执行结果有三:
1:执行:print(func.__name__+'_wrapper_a')和print('*'*20)
    b_wrapper_a
    ********************
2:执行:
def a(*args, **kwargs):
    print('get inner a')
    func(*args, **kwargs)
  定义了一个闭包函数a,此时:func = b,
3:执行:return a
    把以func= b 包裹的闭包函数a作为返回值返回给调用者。
    注意:此时函数a是定义阶段,不会执行函数体代码。

此时的返回值同样是一个闭包函数a,其代码如下:

func = b
def a(*args, **kwargs):
    print('get inner a')
    func(*args, **kwargs)

综上所述:在被装饰函数f加括号调用后,代码运转得到一个返回值,并把这个返回值赋值给变量f,这样就得到了以下的代码:

#函数f
func = b
def a(*args, **kwargs):
    print('get inner a')
    func(*args, **kwargs)

而闭包函数b就是装饰器wrapper_b运行完成的结果

变量f是一个函数,加括号调用,就开始执行代码:

print('get inner a')  #结果:get inner a
func(*args, **kwargs) # func = b,实际执行b(*args, **kwargs)

闭包函数b执行代码:

print('get inner b') #结果:get inner b
func(*args, **kwargs) # func = f,实际执行f(*args, **kwargs)

最后执行被装饰的函数f:

print("hello world") #结果:hello world

因此多个装饰器的执行效果在展现上是从上到下的顺序:

# get inner a
# get inner b
# hello world

但是现在已经可以知道,这只是一种假象,实际的执行来说,是从最近的装饰器开始执行:

# f_wrapper_b
# ********************
# b_wrapper_a
# ********************

上面的执行结果很好的解释了这一点。

因此可以说:

装饰器从执行效果来说是:从上到下;

从实际的执行来说:是从最近的装饰器开始。

posted @ 2018-06-04 23:35  Leslie-x  阅读(128)  评论(0编辑  收藏  举报