Python学习(19):函数式编程-装饰器

我们在开发一个项目的时候,需要尽量遵循这样一个规则:修改是封闭的,扩展是开放的;也就是说在后期更改需求的时候,我们可以不改动以前的代码,而只需要通过扩展就可以满足新需求。Python的装饰器就是帮助我们来实现这一个目标的方法之一。

一、什么是装饰器

通过一个示例来理解装饰器的作用,首先定义一个打印当前时间的函数

import time
def printCurrentTime():
    currentTime = time.time()
    print("当前时间戳:" + str(currentTime))

printCurrentTime()  #打印:当前时间戳:1525522986.449398

考虑这样一个问题:假设我们要增强printCurrentTime()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改printCurrentTime()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。

二、实现一个装饰器

我们使用装饰器来解决上述的问题,装饰器其实就是一个返回函数的高阶函数。下面我们定义一个能打印日志的decorator,装饰器函数pringLog接收一个函数作为参数,并返回一个函数,代码如下:

def pringLog(func):
    def wrapper(*args, **kw):  
        print('---开始调用函数: %s():' % func.__name__)
        result = func(*args, **kw)
        print('---结束调用函数: %s():' % func.__name__)
        return result
    return wrapper
#注意:args与kw是Python中的可变参数,同时使用时,*args在前**kwargs在后
# *args表示任何多个无名参数,它是一个tuple;
# **kwargs表示关键字参数,它是一个dict。

然后我们需要借助Python的@语法,把decorator置于需要增加打印log功能的函数的定义处,并在此测试这个函数:

@pringLog
def printCurrentTime2():
    currentTime = time.time()
    print("当前时间戳:" + str(currentTime))


#测试printCurrentTime2的打印效果
printCurrentTime2()
'''
---开始调用函数: printCurrentTime2():
当前时间戳:1525523570.290186
---结束调用函数: printCurrentTime2():
'''

分析:pringLog()是一个decorator,返回一个函数,原来的printCurrentTime2()函数仍然存在,只是现在同名的printCurrentTime2变量指向了新的函数。

三、装饰器中传入参数

装饰器中也可以传入参数,具体的代码如下:

#定义一个高阶函数,返回装饰器的函数
def printLogWithText(text):
    def pringLog(func):
        def wrapper(*args, **kw):  
            print(text + '---开始调用函数: %s():' % func.__name__)
            result = func(*args, **kw)
            print(text + '---结束调用函数: %s():' % func.__name__)
            return result
        return wrapper
    return pringLog

#使用装饰器
@printLogWithText("装饰器参数")
def printCurrentTime3():
    currentTime = time.time()
    print("当前时间戳:" + str(currentTime))

#测试printCurrentTime3的打印效果
printCurrentTime3()

'''
打印
装饰器参数---开始调用函数: printCurrentTime3():
当前时间戳:1525671291.8881621
装饰器参数---结束调用函数: printCurrentTime3():
'''

四、装饰器引发的函数名问题

经过装饰器后,如果我们在外部打印被装饰的函数名,就会发现它们的名字变化了

print(printCurrentTime2.__name__)  #打印:wrapper
print(printCurrentTime3.__name__)  #打印:wrapper

解决方法:使用Python内置的functools.wraps设置装饰函数

#相当于wrapper.__name__ = func.__name__
import functools
def printInfo(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):  
        print('---开始调用函数: %s():' % func.__name__)
        result = func(*args, **kw)
        print('---结束调用函数: %s():' % func.__name__)
        return result
    return wrapper

@printInfo
def test():
    pass


print(test.__name__)  #打印test

猜你喜欢

转载自blog.csdn.net/dreamcoffeezs/article/details/80648411