使用带参数的外部库装饰器包装

huangapple go评论93阅读模式
英文:

Wrapping a decorator from an external library with arguments

问题

我正在使用cachetools库,并且我想要包装该库的装饰器形式,并添加一个类自身参数以在类级别启用/禁用缓存,例如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标志。

英文:

I'm using the cachetools library and I would like to wrap the decorator form this library and add a class self argument to enable/disable the caching at the class level e.e. MyClass(enable_cache=True)

An example usage would be something like:

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

I'm not sure how to keep the cache as a shared self class object and allow for the enable_cache flag within my own wrapper decorator using this library.

答案1

得分: 5

以下是您要翻译的内容:

If I understand you correctly the answer is actually quite simple - you set the cache to `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)

Output:

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

More flexible you can do it do it by wrapping the cache or by making a whole new decorator:


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 

英文:

If I understand you correctly the answer is actually quite simple - you set the cache to 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)

Output:

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

More flexible you can do it do it by wrapping the cache or by making a whole new decorator:


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 

答案2

得分: 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
英文:

I understand that you want to defer the choice to decorate to the user who will instantiate your class. Then setattr is your friend. I demonstrate its use with a custom decorator, but the principle is the same.

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

答案3

得分: -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_cacheTrue,则calc方法将使用缓存属性operator.attrgetter('cache')使用cachetools.cachedmethod装饰器进行缓存。如果enable_cacheFalse,则将返回未缓存的calc方法。

默认情况下,缓存大小设置为128,但您可以通过修改class_cachedmethod装饰器或装饰器内部的LRUCache实例化中的maxsize参数来进行调整。

英文:

To achieve the desired functionality of enabling or disabling caching at the class level using the cachetools library, you can create a custom decorator that wraps the cachedmethod decorator. Here's an example implementation:

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

In the code above, we define a class_cachedmethod decorator that takes a cache_key argument, which represents the cache attribute name within the class. The decorator returns another decorator that wraps the class and its methods.

Here's how you can use it with your example:

@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

In this example, we apply the class_cachedmethod decorator to the MyClass class, specifying 'cache' as the cache attribute name. The calc method is decorated with the @cache decorator, which internally checks the enable_cache flag and decides whether to apply caching or not.

If enable_cache is True, the calc method will be cached using the cachetools.cachedmethod decorator with the cache attribute operator.attrgetter('cache'). If enable_cache is False, the calc method will be returned without caching.

By default, the cache size is set to 128, but you can adjust it by modifying the maxsize parameter in the class_cachedmethod decorator or in the LRUCache instantiation inside the decorator.

huangapple
  • 本文由 发表于 2023年6月29日 04:50:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76576620.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定