Python基础:函数式编程-装饰器

装饰器

**kwargs用法

**kwargs允许你将不定长度的键值对, 作为参数传递给一个函数。 如果你想要在一个函数里处理带名字的参数, 你应该使用**kwargs

def greet_me(**kwargs):
    for key, value in kwargs.items():
        print("{0} == {1}".format(key, value))

>>> greet_me(name="yasoob")
name == yasoob

装饰器

decorator

比如想增强now()函数的功能——在函数调用前后自动打印日志,又不希望修改now()函数定义,这种在代码运行期间动态增加功能的方式,称为Decorator。

def log(func):
    def wrapper(*args, **kwargs):
        print('call %s():' % func.__name__)  # 注释1
        return func(*args, **kwargs)
    return wrapper

@log
def now():
    print('2019-4-11')

now()

结果将输出:

call now():
2019-4-11

@log定义在now函数之前,相当于执行了now = log(now),这在定义now函数时就已经进行了,由于log()返回一个函数,所以原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。
wrapper()函数的参数定义是(*args,**kwargs),因此wrapper()函数可以接受任意参数的调用以赋值给其内的now()函数。在wrapper()函数内,首先打印日志,再调用原函数。

decorator本身需要传入参数

编写一个返回decorator的高阶函数,如上例子中自定义log的文本:

def log(text):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print('%s %s():' % (text, func.__name__))  # 后面是一个tuple
            return func(*args, **kwargs)
        return wrapper
    return decorator

@log('execute')
def now():
    print('2019-4-11')

now()

3层的嵌套效果是now = log('execute')(now) ,返回的是decorator函数,参数是now函数,最终返回wrapper函数,执行结果如下:

execute now():
2019-4-11

函数签名/完整的decorator

在decorator中,因为log()返回的是wrapper,所以now被包装之后,now.__name__从原来的'now'变成了'wrapper'(无论2层嵌套还是3层嵌套)。这是因为返回的那个wrapper()函数的名字就是'wrapper',所以需要把原始函数的__name__属性复制到wrapper()中,否则有些依赖函数签名的代码执行就会出错。
利用Python内置的functools.wraps可以解决这个问题:

import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('call %s():' % func.__name__)
        return func(*args, **kwargs)
    return wrapper

@log
def now():
    print('2019-4-11')

now()
print(now.__name__)

或者针对带参数的decorator:

import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('%s %s():' % (text, func.__name__))
            return func(*args, **kwargs)
        return wrapper
    return decorator

@log('execute')
def now():
    print('2019-4-11')

now()
print(now.__name__)

既支持@log又支持@log('execute'):

import functools

def log(x):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args,**kwargs):
            print('%s %s():' % (text,func.__name__))
            return func(*args,**kwargs)
        return wrapper
    if isinstance(x,str):
        text = x
        return decorator
    else:
        text = ''
    return decorator(x)

利用decorator打印函数的执行时间

import functools
import time

def metric(fn):
    @functools.wraps(fn)
    def wrapper(*args,**kwargs):
        begin = time.time()
        r = fn(*args,**kwargs)
        end = time.time()
        print('%s executed in %s ms' % (fn.__name__, 1000 * (end - begin)))
        return r
    return wrapper

你可能感兴趣的:(Python基础:函数式编程-装饰器)