装饰器
**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