天天看点

装饰器

As we know,装饰器的作用是在不改变原函数逻辑和调用方式的情况下为函数附加新的功能。

def hello():
    print('hello world!')
    return 1           

一个非常简单的函数,现在在输出hello world!之前输出星号,在输出hello world!之后输出等号,装饰器如下

def deco():
    def inner(func):
        print('*'*12)
        func()
        print('='*12)
    return inner           

原函数使用装饰器

@deco
def hello():
    print('hello world!')
    return 1           

调用函数即可看到如下输出

装饰器

使用@符号相当于执行了以下两步

hello = deco(hello)
hello()           

函数也是一个对象,可以当作参数传递给其它函数,第一步把hello当作参数传递给deco函数,deco返回inner函数,然后在把inner函数赋值给hello,这样的话再次调用hello的时候其实执行的是inner的逻辑,由于把原hello函数当作参数传递给了inner,所以在inner内调用原函数即可。

但是,这样的装饰器是有问题的

原函数返回值

原函数是有返回值的,但是经过装饰器调用之后把inner赋值给了原函数,然而inner无返回值,这就尴尬了,所以要让inner返回原函数的返回值

def deco(func):
    def inner():
        print('*' * 12)
        ret = func()  # 接收原函数返回值
        print('=' * 12)
        return ret
    return inner           

返回值问题解决了,但是,函数名却变了

函数名问题

没使用装饰器的时候

print(hello.__name__)  # 输出hello           

使用装饰器后

@deco
def hello():
    pass
print(hello.__name__)  # 输出inner           

函数名变了,好像也不太对~

解决这个问题,使用内置模块functools改造装饰器

def deco(func):
    @functools.wraps(func)
    def inner():
        ret = func()
        return ret
    return inner           

这样的装饰器只能用于没有参数的函数,对于有参数的函数怎么搞?

函数参数

def deco(func):
    @functools.wraps(func)
    def inner(*args, **kwargs):
        ret = func(*args, **kwargs)
        return ret
    return inner           

因为给函数使用装饰器后,原函数其实就变成了装饰器的内部函数,那么原函数的参数就是传递给装饰器的内部函数,装饰器的内部函数就要能够接收参数

给装饰器指定参数

def deco_params(*arg, **kw):
    def deco(func):
        @functools.wraps(func)
        def inner(*args, **kwargs):
            ret = func(*args, **kwargs)
            return ret
        return inner
    return deco           

使用装饰器

@deco_params(1, 2)
def hello(a, b):
    pass           

调用过程

deco = deco_params(1, 2)
hello = deco(hello)
hello(3, 4)           

继续阅读