簡體   English   中英

Python:如何忽略 functools.lru_cache 中的特殊參數?

[英]Python: How can I ignore a special parameter in functools.lru_cache?

我想要緩存的函數是這樣的:

def a(x, time='last'):

我對每個a(x,y)都有確定性行為,除非y=='last' 所以當a(x, 'last')被調用時,我想為其他所有東西調用“真實的東西”和一個 lru_cached 函數。

我想這可以用我自己的裝飾器實現:

def my_lru_cache(func):
    def function_wrapper(*args, **kwargs):
        if kwargs is not None:
            if 'time' in kwargs:
                return func(*args, **kwargs)
            else:
                return what?!?

    return function_wrapper

我完全錯了嗎? 這怎么可能?

將函數包裝在lru_cache() ,然后在頂部添加裝飾器,並通過__wrapped__屬性訪問原始未緩存的函數,或者更好的方法是,使用inspect.unwrap()函數剝離任意數量的裝飾器的功能:

from functools import wraps
from inspect import unwrap

def bypass_cache_last_time(func):
    @wraps(func)
    def function_wrapper(*args, **kwargs):
        if not 'time' in kwargs or kwargs['time'] == 'last':
            # Bypass any additional decorators and call function directly
            return unwrap(func)(*args, **kwargs)
        else:
            return func(*args, **kwargs)

        return function_wrapper

並以此作為

@bypass_cache_last_time
@lru_cache()
def some_function(x, time='last'):
    # ...

functools.wraps()裝飾器通過重新包裝器的能力,因為它在包裝器上設置了__wrapped__屬性。

或者讓您的裝飾器應用lru_cache()裝飾器本身,並在裝飾時保留原始函數的自己的副本:

def my_lru_cache(func):
    cached = lru_cache()(func)

    @wraps(func)
    def function_wrapper(*args, **kwargs):
        if not 'time' in kwargs or kwargs['time'] == 'last':
            # call the function directly
            return func(*args, **kwargs)
        else:
            # use the lru_cache-wrapped version
            return cached(*args, **kwargs)

    return function_wrapper

將此用作

@my_lru_cache
def some_function(x, time='last'):
    # ...

您可以使用lru_cache(<args>)(func)直接調用lru_cache()以獲取“包裝”版本的func 然后,您可以從包裝器中將其返回:

def my_lru_cache(func):
    caching_func = lru_cache()(func)
    def function_wrapper(*args, **kwargs):        
        if kwargs.get('time') == 'last':
            return func(*args, **kwargs)
        return caching_func(*args, **kwargs)
    return function_wrapper

改進@martijn-pieters 對我的一個用例的回答; 在這里,我創建了一個包裝器,它適用於任何可以傳遞kwarg skip_cache以指定是返回緩存值還是再次計算該值的函數。

from functools import lru_cache, wraps


def skippable_lru_cache(maxsize: int = 128, typed: bool = False):
    def wrapper_cache(func):
        cached_func = lru_cache(maxsize=maxsize, typed=typed)(func)

        @wraps(func)
        def wrapped_func(*args, **kwargs):
            if 'skip_cache' in kwargs and kwargs['skip_cache'] == True:
                # call the function directly
                return func(*args, **kwargs)
            else:
                # Remove skip_cache from kwargs so that its value doesn't affect stored results
                try:
                    del kwargs['skip_cache']
                except:
                    pass
                # use the lru_cache-wrapped version
                return cached_func(*args, **kwargs)

        wrapped_func.cache_info = cached_func.cache_info

        return wrapped_func

    return wrapper_cache


@skippable_lru_cache(maxsize=128)
def calc(v1: int, v2: int, skip_cache: bool = False):
    return v1 * v2


print('Run 1:')
for i in range(20):
    print(calc(i, 10))
print(calc.cache_info())

print('Run 2:')
for i in range(20):
    print(calc(i, 10, skip_cache=i > 9))
print(calc.cache_info())

結果:

Run 1:
0
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
CacheInfo(hits=0, misses=20, maxsize=128, currsize=20)
# All Run 1 calls are supposed to be hitting a miss as they are requesting cached value, but none are cached, generating a cache of size 20

Run 2:
0
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
CacheInfo(hits=10, misses=20, maxsize=128, currsize=20)
# For Run 2, only first 10 calls are requesting cache value, which shows in CacheInfo as hits=10 while still keeping the cache size at 20 although skip_cache is present with different value. For the second 10 calls, cache is completely being skipped resulting in the calls not registering as hit or miss, nor affecting the cache size

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM