使用装饰函数作为函数的默认参数

Using a decorated function as a function's default argument

考虑这个模块:

#mymodule.py
import logging

def print_start_end(name):
    """
    Decorator that creates a logger and logs the start 
    and the end of the function call
    """
    def decorator(f):
        logger = logging.getLogger(name)
        def wrapper(*args, **kwargs):
            logger.info("start")
            res = f(*args, **kwargs)
            logger.info("end")
            return res
        return wrapper
    return decorator

@print_start_end(__name__)
def f(x):
    return x

def g(y=f(3)):
    return y

还有一个示例脚本:

import logging
from mymodule import f

logger = logging.getLogger("")
logger.setLevel(logging.INFO)
h = logging.StreamHandler()
h.setLevel(logging.INFO)
logger.addHandler(h)

print f(3)

输出:

start
end
3

装饰器正在工作。但是现在我写了一个脚本来使用 g 而不是 f:

import logging
from mymodule import g

logger = logging.getLogger("")
logger.setLevel(logging.INFO)
h = logging.StreamHandler()
h.setLevel(logging.INFO)
logger.addHandler(h)

print g()

输出:

3

在执行语句print g()时,对f的调用是成功的,因为它打印了预期的return值3。但是为什么它不也打印"start" 和 "end"?

问题是默认值被立即评估,记录器被初始化之前。解决这个问题的方法是将执行推迟到之后。

def g(y=lambda:f(3)):
    return y()

注意def g(y=f(3)):中的f(3)在定义函数的时候只执行一次,不是每次调用的时候都执行

因此,问题似乎是在 mymodule 中执行 f(3) 时,记录器尚未初始化。先初始化,再导入,即可运行:

import logging

logger = logging.getLogger("")
logger.setLevel(logging.INFO)
h = logging.StreamHandler()
h.setLevel(logging.INFO)
logger.addHandler(h)

from mymodule import g

print g()