`matplotlib._logged_cached` 是如何工作的?
How does `matplotlib._logged_cached` work?
以下代码来自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
的局部变量不会在每次调用时重新初始化吗?
据我了解,局部变量在函数 returns 之后被删除(对吧?)。如果是这样,那么缓存将不起作用。然而,文档说装饰函数将在每次调用时 return 相同的对象——它确实如此。怎么样?
缓存起作用的原因是因为一个叫做闭包的概念。您的理解是正确的,局部变量在函数退出后被删除,但此处用于缓存的变量不是内部函数的局部变量。
如果您在函数上使用 _logged_cached
装饰器,该函数实质上将被嵌套函数 wrapper
替换。这个 wrapper
函数在返回时变成一个闭包(一个函数对象),它将记住封闭范围内的非局部变量。所以它本质上将有一个记住变量 called
和 ret
的映射。每次调用函数对象(修饰函数)时,内部范围将有 called
和 ret
可供使用。
通常它们只能在内部函数中用作只读,但通过使用关键字 nonlocal
,内部函数实际上能够 change/update 变量的值 called
和 ret
在闭包中。因此,我们之前提到的保存在函数对象中的映射会更新其值,并且每次对装饰函数的后续调用都将使用这些相同的值。
以下代码来自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
的局部变量不会在每次调用时重新初始化吗?
据我了解,局部变量在函数 returns 之后被删除(对吧?)。如果是这样,那么缓存将不起作用。然而,文档说装饰函数将在每次调用时 return 相同的对象——它确实如此。怎么样?
缓存起作用的原因是因为一个叫做闭包的概念。您的理解是正确的,局部变量在函数退出后被删除,但此处用于缓存的变量不是内部函数的局部变量。
如果您在函数上使用 _logged_cached
装饰器,该函数实质上将被嵌套函数 wrapper
替换。这个 wrapper
函数在返回时变成一个闭包(一个函数对象),它将记住封闭范围内的非局部变量。所以它本质上将有一个记住变量 called
和 ret
的映射。每次调用函数对象(修饰函数)时,内部范围将有 called
和 ret
可供使用。
通常它们只能在内部函数中用作只读,但通过使用关键字 nonlocal
,内部函数实际上能够 change/update 变量的值 called
和 ret
在闭包中。因此,我们之前提到的保存在函数对象中的映射会更新其值,并且每次对装饰函数的后续调用都将使用这些相同的值。