Pythons lru_cache 在内部函数上似乎不起作用

Mau*_*ice 11 python caching python-3.x

我试图用来functools.lru_cache缓存内部函数的结果,但缓存似乎没有按预期工作。

我有一个函数执行一些逻辑,然后调用一个相当昂贵的函数。我想缓存昂贵的函数调用的结果,尽管我只是将其应用于lru_cache内部函数。不幸的是,行为并不符合预期——即使内部函数的参数相同,每次都会调用昂贵的函数。

我创建了一个(简化的)测试用例来显示行为:

import unittest
from functools import lru_cache
from unittest.mock import patch

def expensive_function(parameter: str) -> str:
    return parameter

def partially_cached(some_parameter: str) -> str:

    @lru_cache
    def inner_function(parameter: str):
        return expensive_function(parameter)

    result = inner_function(some_parameter)

    print(inner_function.cache_info())

    return result

class CacheTestCase(unittest.TestCase):

    def test_partially_cached(self):
        
        with patch(self.__module__ + ".expensive_function") as expensive_mock:

            expensive_mock.return_value = "a"

            self.assertEqual(partially_cached("a"), "a")
            self.assertEqual(partially_cached("a"), "a")

            # If the cache works, I expect the expensive function
            # to be called just once for the same parameter
            expensive_mock.assert_called_once()

if __name__ == "__main__":
    unittest.main()
Run Code Online (Sandbox Code Playgroud)

(在这种情况下,我不需要内部函数,但正如我所说 - 它被简化了)

不幸的是测试失败了

python3 /scratch/test_cache.py
CacheInfo(hits=0, misses=1, maxsize=128, currsize=1)
CacheInfo(hits=0, misses=1, maxsize=128, currsize=1)
F
======================================================================
FAIL: test_partially_cached (__main__.CacheTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
[...]
AssertionError: Expected 'expensive_function' to have been called once. Called 2 times.
Calls: [call('a'), call('a')].

----------------------------------------------------------------------
Ran 1 test in 0.004s

FAILED (failures=1)
Run Code Online (Sandbox Code Playgroud)

我可能对内部函数或 lru_cache 有误解,但我不确定它是什么 - 我感谢我能得到的所有帮助。

pro*_*ico 8

它无法按预期工作,因为inner_function每次partially_cached调用时都会重新定义 gets ,缓存版本也是如此。因此每个缓存版本仅被调用一次。

请参阅memoizing-decorator-keeping-stored-values

此外,如果您模拟装饰函数,则需要再次应用装饰器。

请参阅如何模拟装饰函数

如果您在外部级别进行装饰,或者装饰另一个提取到外部级别的函数,如果您不通过模拟破坏它,它将起作用。