我正在使用
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标志。
如果我理解正确的话,答案实际上很简单 - 你将缓存设置为
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
我理解您希望将装饰的选择推迟给将实例化您的类的用户。那么
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
要使用
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
参数来调整它。