Python 函数装饰器

  • 2020-04-05
  • 1,449
  • 0

一、装饰器有什么用

函数装饰器是修改其他函数的功能的函数,不用修改原来的函数就可以增加新的功能,并且可以大量复用代码。装饰器有助于让我们的代码更Pythonic,语义更加清晰!

二、函数装饰器举例

def logging_fuc(func):
    def wrapper(*args, **Kwargs):
        print('[log] this is a log')
        return func(*args, **Kwargs)
    return wrapper

def mywork():
    print('I am doing my work!')

if __name__ == '__main__':
    mywork = logging_fuc(mywork)
    mywork()

结果:

[log] this is a log
I am doing my work!

分析:

具体逻辑是将原函数作为参数传递给一个新函数,新函数里面有一个内置函数,内置函数 先实现了额外功能,再在return中执行了传入的函数,最后这个logging_fuc 函数返回了内部函数。

装饰器通过将原函数名重新指向logging_fuc 的内部函数 wrapper ,所以有这样的逻辑顺序,先执行新功能,再执行原函数(指原mywork方法)。

以上是装饰器的原始形态,还有一个将mywork重新赋值的过程。在通常的表示中 装饰器常常不这么显示(因为太麻烦了!!)而是在mywork函数上方用@logging_func标示。如下

def logging_fuc(func):
    def wrapper(*args, **Kwargs):
        print('[log] this is a log')
        return func(*args, **Kwargs)
    return wrapper

@logging_fuc
def mywork():
    print('I am doing my work!')

if __name__ == '__main__':
    mywork()

结果:

[log] this is a log
I am doing my work!

 

这下装饰器的名字名副其实了,用@xxxx装饰了mywork!

 

三、含参数的函数装饰器

emmm赤裸裸的套娃,直接上例子

def logging_fuc(other_str='default'):
    def decorator(func):
        def wrapper(*args, **Kwargs):
            print('[log] this is a log')
            print(other_str)
            return func(*args, **Kwargs)
        
        return wrapper
    return decorator


@logging_fuc(other_str='this is my work')
def mywork(work='count'):
    print('I am doing my work!', work)


if __name__ == '__main__':
    mywork()

结果:

[log] this is a log
this is my work
I am doing my work! count

首先装饰器logging_fuc 可以有参数,要理解通常额外的逻辑在最内层函数,logging_fuc参数可以被内层函数使用,现在从最外层函数开始看,logging_fuc返回了decorator,mywork指向decorator,dacorator运行是return 了 wrapper,mywork运行时指向了wrapper,最终mywork运行时实现了wrapper功能,最终func()在return时被调用,完成mywork原功能。

四、被装饰函数带返回值该如何处理

def logging_fuc(other_str='default'):
    def decorator(func):
        def wrapper(*args, **Kwargs):
            print('[log] this is a log')
            print(other_str)
            res = func(*args, **Kwargs)
            return res
        return wrapper
    return decorator

@logging_fuc(other_str='this is my work')
def mywork(work='count'):
    print('I am doing my work!', work)
    return True

if __name__ == '__main__':
    res = mywork()
    print(res)

结果:

[log] this is a log
this is my work
I am doing my work! count
True

这里最需要理解的就是,其实装饰器函数最内层return fuc()是带括号的,带括号意味着运行函数,之前在没有返回值的函数直接运行即可,现在有返回值,只需要在return之前先执行,用变量接着返回值,return 返回值即可。

 

五、函数装饰器缺点

函数装饰器的优点是可以拓展原函数内容,同时使用函数装饰器也有缺点,就是函数装饰器会使一些原函数的性质丢失。

def logging_fuc(other_str='default'):
    def decorator(func):
        def wrapper(*args, **Kwargs):
            print('[log] this is a log')
            print(other_str)
            res = func(*args, **Kwargs)
            return res
        return wrapper
    return decorator

@logging_fuc(other_str='this is my work')
def mywork(work='count'):
    '''
    :param work:
    :return:
    '''
    print('I am doing my work!', work)
    return True

if __name__ == '__main__':
    res = mywork()
    print('fun name:', mywork.__name__)
    print('fun doc:', mywork.__doc__)
    print(res)

结果:

[log] this is a log
this is my work
I am doing my work! count
fun name: wrapper
fun doc: None
True

函数名变成了内层函数函数名,原函数的注释也木得了。那么如何解决这个问题呢?

使用functiontools的装饰器

import functools


def logging_fuc(other_str='default'):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **Kwargs):
            print('[log] this is a log')
            print(other_str)
            res = func(*args, **Kwargs)
            return res

        return wrapper

    return decorator


@logging_fuc(other_str='this is my work')
def mywork(work='count'):
    '''
    :param work:
    :return:
    '''
    print('I am doing my work!', work)
    return True


if __name__ == '__main__':
    res = mywork()
    print('fun name:', mywork.__name__)
    print('fun doc:', mywork.__doc__)
    print(res)

结果:

[log] this is a log
this is my work
I am doing my work! count
fun name: mywork
fun doc: 
    :param work:
    :return:
    
True

原函数的信息被正常输出了!

评论

还没有任何评论,你来说两句吧

苟活时长: Copyright © 2019-2020 OJO