Python装饰器
一、不接受参数的装饰器
装饰器的例子:
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
>>> @timethis
... def countdown(n):
... '''
... Counts down
... '''
... while n > 0:
... n -= 1
...
>>> countdown(100000)
countdown 0.008917808532714844
>>> countdown(10000000)
countdown 0.87188299392912
>>>
讨论
一个装饰器就是一个函数,它接受一个函数作为参数并返回一个新的函数。 当你像下面这样写:
@timethis
def countdown(n):
pass
跟像下面这样写其实效果是一样的:
def countdown(n):
pass
countdown = timethis(countdown)
顺便说一下,内置的装饰器比如 @staticmethod, @classmethod,@property
原理也是一样的。 例如,下面这两个代码片段是等价的:
class A:
@classmethod
def method(cls):
pass
class B:
# Equivalent definition of a class method
def method(cls):
pass
method = classmethod(method)
二、接受参数的装饰器
装饰器的例子:
from functools import wraps
import logging
def logged(level, name=None, message=None):
"""
Add logging to a function. level is the logging
level, name is the logger name, and message is the
log message. If name and message aren't specified,
they default to the function's module and name.
"""
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()
的参数的。
讨论
定义一个接受参数的包装器看上去比较复杂主要是因为底层的调用序列。特别的,如果你有下面这个代码:
@decorator(x, y, z)
def func(a, b):
pass
装饰器处理过程跟下面的调用是等效的;
def func(a, b):
pass
func = decorator(x, y, z)(func)
decorator(x, y, z)
的返回结果必须是一个可调用对象,它接受一个函数作为参数并包装它
结语
见识了装饰器的等效实现之后,对接收参数的装饰器豁然开朗,只需要增加最外层接收参数即可;decorator(x, y, z)(func)