【问题标题】:Using a decorated function as a function's default argument使用修饰函数作为函数的默认参数
【发布时间】:2015-09-12 05:22:41
【问题描述】:

考虑这个模块:

#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是成功的,因为它打印了预期的返回值3。但是为什么它不打印“开始”和“结束”呢?

【问题讨论】:

  • 这是你的记录器,我几乎完全确定。我这么说的原因是,如果你在包装器中添加一个 print 语句,你可以看到它被 f(3) 调用。

标签: python python-2.7 logging decorator python-decorators


【解决方案1】:

注意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()

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 但是使用lambda 作为默认值也很奇怪。如果我传递一个实际的整数参数怎么办?相反,也许做一些类似def g(y=None): if y is None: y = f(3) 的事情。通过使用lambda,您可以将参数的类型隐式更改为“可调用”。
    • 当然,这也推迟了评估。
    猜你喜欢
    • 2019-09-11
    • 2023-02-10
    • 1970-01-01
    • 2017-08-03
    • 2012-02-03
    • 1970-01-01
    • 1970-01-01
    • 2016-09-27
    • 1970-01-01
    相关资源
    最近更新 更多