9.4 定义一个带�数的装饰器¶

问题¶

你想定义一个�以接��数的装饰器

解决方案¶

我们用一个例�详细�述下接��数的处�过程。 �设你想写一个装饰器,给函数添加日志功能,�时�许用户指定日志的级别和其他的选项。 下�是这个装饰器的定义和使用示例:

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) 的返回结果必须是一个�调用对象,它接�一个函数作为�数并包装它, �以�考9.7�节中�外一个�接��数的包装器例�。