使用参数从外部库包装装饰器 - 使缓存可选

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

我正在使用

cachetools
库,我想包装该库中的装饰器,并添加一个类 self 参数以启用/禁用类级别的缓存,例如
MyClass(enable_cache=True)

示例用法如下:

class MyClass(object):
    def __init__(self, enable_cache=True):
        self.enable_cache = enable_cache
        self.cache = cachetools.LRUCache(maxsize=10)
    
    @cachetools.cachedmethod(operator.attrgetter('cache'))
    def calc(self, n):
        return 1*n

我不确定如何将缓存保留为共享自类对象,并允许使用此库在我自己的包装器装饰器中使用enable_cache标志。

python caching decorator wrapper
3个回答
5
投票

如果我理解正确的话,答案实际上很简单 - 你将缓存设置为

None

import cachetools
import operator

class MyClass(object):
    def __init__(self, enable_cache=True):
        self.cache = cachetools.LRUCache(maxsize=10) if enable_cache else None
    
    @cachetools.cachedmethod(operator.attrgetter('cache'))
    def calc(self, n):
        print("Calculating", n)
        return 1*n
    
    
m1 = MyClass(True)
m1.calc(2)
m1.calc(2)
m1.calc(3)
m1.calc(3)
print("now without")
m2 = MyClass(False)
m2.calc(2)
m2.calc(2)
m2.calc(3)
m2.calc(3)

输出:

Calculating 2
Calculating 3
now without
Calculating 2
Calculating 2
Calculating 3
Calculating 3

更灵活,您可以通过包装缓存或制作全新的装饰器来做到这一点:


import cachetools
import operator

def flexible_cache(cache):
    def cache_wrapper(self):
        if self.enable_cache:
            return cache(self)
        return None
    return cache_wrapper

def optional_cache(cache, *args, **kwargs):
    return cachetools.cachedmethod(flexible_cache(cache), *args, **kwargs)
    

class MyClass(object):
    def __init__(self, enable_cache=True):
        self.enable_cache = enable_cache
        self.cache = cachetools.LRUCache(maxsize=10) # Now the None part is handled by the decorators
    
    @cachetools.cachedmethod(flexible_cache(operator.attrgetter('cache')))
    def calc2(self, n):
        print("Calculating2", 2*n)
        return 2*n
    
    @optional_cache(operator.attrgetter('cache'))
    def calc3(self, n):
       print("Calculating3", 2*n)
       return 2*n 


0
投票

我理解您希望将装饰的选择推迟给将实例化您的类的用户。那么

setattr
就是你的朋友。我用自定义装饰器演示了它的使用,但原理是相同的。

def decorator(func):
    def inner(*args, **kwargs):
        result = func(*args, **kwargs)
        print(f"{result = }")
        return result
    return inner

class Asd:
    def __init__(self, decorate=False):
        if decorate:
            setattr(self, "method", decorator(getattr(self, "method")))
    
    def method(self):
        return 42
>>> asd = Asd(decorate=False)
>>> asd.method()
42
>>> asd = Asd(decorate=True)
>>> asd.method()
result = 42
42

-1
投票

要使用

cachetools
库实现在类级别启用或禁用缓存的所需功能,您可以创建一个包装
cachedmethod
装饰器的自定义装饰器。这是一个示例实现:

import cachetools
import operator

def class_cachedmethod(cache_key, maxsize=128):
    def decorator(cls):
        cls.cache = cachetools.LRUCache(maxsize=maxsize)
        
        def wrapper(method):
            if not getattr(cls, 'enable_cache', True):
                return method
            return cachetools.cachedmethod(operator.attrgetter(cache_key))(method)
        
        setattr(cls, cache_key, wrapper)
        return cls
    return decorator

在上面的代码中,我们定义了一个

class_cachedmethod
装饰器,它接受
cache_key
参数,该参数表示类中的缓存属性名称。装饰器返回另一个包装类及其方法的装饰器。

以下是如何在示例中使用它:

@class_cachedmethod('cache')
class MyClass(object):
    def __init__(self, enable_cache=True):
        self.enable_cache = enable_cache

    def cache(self, method):
        return method

    @cache
    def calc(self, n):
        return 1 * n

在此示例中,我们将

class_cachedmethod
装饰器应用于
MyClass
类,指定
'cache'
作为缓存属性名称。
calc
方法使用
@cache
装饰器进行装饰,该装饰器在内部检查
enable_cache
标志并决定是否应用缓存。

如果

enable_cache
True
,则将使用具有缓存属性
calc
cachetools.cachedmethod
装饰器来缓存
operator.attrgetter('cache')
方法。如果
enable_cache
False
,则将返回
calc
方法,而不进行缓存。

默认情况下,缓存大小设置为 128,但您可以通过修改

maxsize
装饰器中或装饰器内的
class_cachedmethod
实例化中的
LRUCache
参数来调整它。

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