【问题标题】:How does `matplotlib._logged_cached` work?`matplotlib._logged_cached` 是如何工作的?
【发布时间】:2023-03-07 16:27:02
【问题描述】:

以下代码来自 Matplotlib 的__init__ 文件:

def _logged_cached(fmt, func=None):
    """
    Decorator that logs a function's return value, and memoizes that value.

    After ::

        @_logged_cached(fmt)
        def func(): ...

    the first call to *func* will log its return value at the DEBUG level using
    %-format string *fmt*, and memoize it; later calls to *func* will directly
    return that value.
    """
    if func is None:  # Return the actual decorator.
        return functools.partial(_logged_cached, fmt)

    called = False
    ret = None

    @functools.wraps(func)
    def wrapper(**kwargs):
        nonlocal called, ret
        if not called:
            ret = func(**kwargs)
            called = True
            _log.debug(fmt, ret)
        return ret

    return wrapper

问题:上述函数中的缓存是如何工作的? _logged_cached 的局部变量不会在每次调用时重新初始化吗?

据我了解,局部变量在函数返回后被删除(对吗?)。如果是这样,那么缓存将不起作用。然而,文档说装饰函数将在每次调用时返回相同的对象——它确实如此。怎么样?

【问题讨论】:

    标签: python matplotlib decorator local-variables


    【解决方案1】:

    缓存起作用的原因是一个称为闭包的概念。你的理解是正确的,局部变量在函数退出后被删除,但这里用于缓存的变量不是内部函数的本地变量。

    如果您在函数上使用_logged_cached 装饰器,则该函数本质上将被嵌套函数wrapper 替换。这个wrapper 函数在返回时成为一个闭包(一个函数对象),它将记住封闭范围内的非局部变量。所以它本质上会有一个映射来记住变量calledret。每次调用函数对象(修饰函数)时,内部作用域都会有 calledret 可供使用。

    通常它们只能在内部函数中用作只读,但通过使用关键字 nonlocal,内部函数实际上能够更改/更新变量 calledret 中的值关闭。因此,我们之前提到的保存在函数对象中的映射会更新其值,并且对修饰函数的每次后续调用都将使用这些相同的值。

    【讨论】:

    • 对于那些想知道这些变量究竟存储在哪里的人来说,它在wrapper.__closure__ 中(其中wrapper 是使用函数对象作为func 参数调用_logged_cached 时返回的函数)。在这种情况下,wrapper.__closure__ 将是 Cell 对象的元组。
    猜你喜欢
    • 2012-10-23
    • 2021-12-11
    • 2020-11-02
    • 2015-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-14
    • 2021-07-06
    相关资源
    最近更新 更多