如何创建@iterator_cache装饰器?

问题描述 投票:0回答:1

问题: 如何创建

iterator_cache
装饰器?

@iterator_cache
def cheap_eval(numbers: Tuple[int]) -> Tuple[int]:
    print("cheap eval of {numbers}")
    return expensive_eval(numbers)

def expensive_eval(numbers: Tuple[int]) -> Tuple[int]:
    time.sleep(5 + len(numbers))  # Example of consuming time
    print(f"expensive eval of {numbers}")
    return tuple(numb**2 for numb in numbers)

描述:我有库中的

expensive_eval
函数,我需要多次调用它。

我想创建使用缓存的

cheap_eval
并提供与
expensive_eval
相同的结果以减少总时间。

我的第一个想法是使用辅助函数

eval_one
@lru_cache
,但是为每个值调用
expensive_eval
并不好,因为每次调用
expensive_eval
都有 5 秒的常数项。

如何创建装饰器,使其记住所有可迭代输入而不是整个元组?

具有输入和所需输出的代码是:

# Input
print(cheap_eval([1, 2]))
print(cheap_eval([1, 3]))
print(cheap_eval([1, 2, 3]))

# Output without iterator_cache
# total time = 22 seconds
cheap eval of [1, 2]
expensive eval of [1, 2]
[1, 4]
cheap eval of [1, 3]
expensive eval of [1, 3]
[1, 9]
cheap eval of [1, 2, 3]
expensive eval of [1, 2, 3]
[1, 4, 9]

# Output with iterator_cache
# total time = 13 seconds
cheap eval of [1, 2]
expensive eval of [1, 2]
[1, 4]
cheap eval of [1, 3]
expensive eval of [3]
[1, 9]
cheap eval of [1, 2, 3]
[1, 4, 9]
python caching
1个回答
0
投票

因此,只需分离出未缓存的输入并在未见过的输入上调用昂贵的函数即可。使用它来更新缓存。然后使用缓存来填充输出。

from collections.abc import Sequence

_cache = {}
def cheap_eval(numbers: Sequence[int]) -> list[int]:
    new = []

    for n in numbers:
        if n not in _cache:
            new.append(n)

    new_results = expensive_eval(new)
    
    for n, r in zip(new, new_results):
        _cache[n] = r

    return [_cache[n] for n in numbers]
            
    

如果需要,您可以将上述逻辑抽象为装饰器。但这是基本想法。

© www.soinside.com 2019 - 2024. All rights reserved.