装饰器
在函数上添加一个装饰器,增加额外的操作处理(比如日志、计时等)。
使用装饰器计算耗时案例如下:
import time
from functools import wraps
def timethis(func):
'''
Decorator that reports the execution time.
'''
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
return wrapper
注:使用 @wraps 装饰器。 通过底层的
__wrapped__
属性访问到函数签名信息
使用装饰器的例子:
>>> @timethis
... def countdown(n):
... while n > 0:
... n -= 1
>>> countdown(100000)
countdown 0.008917808532714844
>>> countdown(10000000)
countdown 0.87188299392912
等价于:
def countdown(n):
pass
countdown = timethis(countdown)
在上面的 wrapper() 函数中, 装饰器内部定义了一个使用 *args 和 **kwargs 来接受任意参数的函数。 在这个函数里面调用了原始函数并将其结果返回,不过你还可以添加其他额外的代码(比如计时)。 然后这个新的函数包装器被作为结果返回来代替原始函数
带参数的装饰器
假设想写一个装饰器,给函数添加日志功能,同时允许用户指定日志的级别和其他的选项, 示例如下:
from functools import wraps
import logging
def logged(level, name=None, message=None):
def decorate(func):
logname = name if name else func.__module__
log = logging.getLogger(logname)
logmsg = message if message else func.__name__
@wraps(func)
def wrapper(*args, **kwargs):
log.log(level, logmsg)
return func(*args, **kwargs)
return wrapper
return decorate
# Example use
@logged(logging.DEBUG)
def add(x, y):
return x + y
@logged(logging.CRITICAL, 'example')
def spam():
print('Spam!')
- logged() 接受参数并将它们作用在内部的 器函数上面。
- 内层的函数 decorate() 接受一个函数作为参数,然后在函数上面放置一个包装器。 这里的关键点是包装器是可以使用传递给 logged() 的参数的
- 定义装饰器的时候,应该使用 functools 库中的
@wraps
装饰器来注解底层包装函数,让装饰器去直接复制原始函数的参数签名信息。 @wraps
有一个重要特征是它能让你通过属性__wrapped__
直接访问被包装函数,add.__wrapped__(2,3)
带可选参数的装饰器
可以不传参数给它,比如 @decorator
, 也可以传递可选参数给它,比如 @decorator(x,y,z)
from functools import wraps, partial
import logging
def logged(func=None, *, level=logging.DEBUG, name=None, message=None):
if func is None:
return partial(logged, level=level, name=name, message=message)
logname = name if name else func.__module__
log = logging.getLogger(logname)
logmsg = message if message else func.__name__
@wraps(func)
def wrapper(*args, **kwargs):
log.log(level, logmsg)
return func(*args, **kwargs)
return wrapper
# Example use
@logged
def add(x, y):
return x + y
@logged(level=logging.CRITICAL, name='example')
def spam():
print('Spam!')
functools.partial()
需要减少某个函数的参数个数,你可以使用 functools.partial() 。 partial() 函数允许你给一个或多个参数设置固定的值,减少接下来被调用时的参数个数。
def spam(a, b, c, d):
print(a, b, c, d)
>>> from functools import partial
>>> s1 = partial(spam, 1) # a = 1
>>> s1(2, 3, 4)
1 2 3 4
>>> s2 = partial(spam, d=42) # d = 42
>>>> s2(1, 2, 3)
1 2 3 42
固定某些参数并返回一个新的callable对象。这个新的callable接受未赋值的参数, 然后跟之前已经赋值过的参数合并起来,最后将所有参数传递给原始函数。