Python mixin/decorator/__metaclass__ 基础 class 增强

Python mixin/decorator/__metaclass__ for base class enhancement

我正在为 Django REST API 实现内容感知缓存系统。我想开发一个可以添加到现有视图的组件,该组件将通过检查缓存并在未命中时回退到基础 class 行为来修改基础 class 的行为。

基本上,我有这样的东西:

class Base:
   def get(self, request, *args, **kwargs):
       ....
       return Response

class AnotherBase:
   def get(self, request, *args, **kwargs):
       .... 
       return Response

class Derived(Base):
    pass

class OtherDerived(AnotherBase):
    pass

我最初的想法是按照

class Cacheable:
    def get(self, request, *args, **kwargs):
       cache_key = self.get_cache_key(request)
       base_get = #.... and this is the problem
       return cache.get(cache_key, base_get(request, *args, **kwargs))

    def get_cache_key(self, request):
       # .... do stuff

class Derived(Cacheable, Base):
    pass

class AnotherDerived(Cacheable, AnotherBase):
    pass

很明显这是行不通的,因为我不知道如何,或者是否可能,或者是否建议从混入中访问同级 superclass(es)。

我的目标是允许我在不触及现有 classes 的内部结构的情况下向现有视图添加缓存行为的实现。 给定一个视图 class、C、s.t。 C.get(request, *args, **kwargs) -> Response,有没有函数,F,s.t。 F(C).get(... 回退到 C.get 之前是否进行缓存检查?在这个准正式的表示法中,我们会说在 class 定义中向最左边的父 class 添加一个 mixin 算作一个函数。

使用方法装饰器是否更合适?或者 class 装饰器如何工作?

然后我在研究这个时看到了对 __metaclass__ 的引用,但我不清楚这种方法是什么样的。

这是Python3.6

我的两分钱:

  1. 您正在走进一个不为人知的领域。熟悉所有相关概念,尝试一些,然后再决定。
  2. Here 是关于元类的很好的教程。
  3. Here 有一个关于装饰器的。
  4. 我与那个网站没有任何关系。

简单示例:

def Base:

    def _get_data(self):
        # get the data eg from database
        return self._get_data_native()

    def get(self, request, *args, **kwargs):
        return Response(self._get_data())

def Cacheable(Base):

    def _get_data(self):
        # get from cache ...
        result = ...
        if result is None:
            # or from base ...
            result = ...

        return result

def Derived(Cacheable):

    def _get_data_native(self):
        # get the data eg from database
        ...

通过从 Cacheable 继承,您在这里包含了缓存,因为 _get_data 在那里被覆盖了。

对于这个问题,如果你只想在一个地方添加缓存,你不需要元类或装饰器。

当然,可以使用装饰器以更通用的方式包含缓存。

例如这个答案:Is there a decorator to simply cache function return values?

答案是装饰器和一些 Django 特定的库。

from django.utils.decorators import method_decorator
from django.core.cache import cache

def cached_get(cache_key_func=None):
    """
    Decorator to be applied via django.utils.decorators.method_decorator
    Implements content-aware cache fetching by decorating the "get" method
    on a django View
    :param cache_key_func: a function of fn(request, *args, **kwargs) --> String
    which determines the cache key for the request
    """
    def decorator(func):
        def cached_func(request, *args, **kwargs):
            assert cache_key_func is not None, "cache_key_function is required"
            key = cache_key_func(request, *args, **kwargs)
            result = cache.get(key)
            if result is None:
                return func(request, *args, **kwargs)
            return Response(result)
        return cached_func
    return decorator

@method_decorator(cached_get(cache_key_func=get_cache_key), name="get")
class SomeView(BaseView):
    ...

def get_cache_key(request):
    # do arbitrary processing on request, the following is the naïve melody
    key =  urllib.urlencode(request.query_params)
    return key 

所以解决方案是使用 Django 的内置 method_decorator,它将其第一个参数装饰器应用于装饰的 class 方法,该方法由第二个参数命名,name,至 method_decorator。我定义了一个高阶函数 cached_get,它以另一个函数作为参数,returns 是一个柯里化函数(所谓的闭包)。通过使用函数 get_cache_key 调用它(请注意,不是 调用 该函数)我有一个装饰器将应用于 'get' 方法SomeView

装饰器本身是一个简单的 Python 装饰器——在这个应用程序中,它是 cached_func 而原始的、未装饰的 get 方法是 func。因此,cached_func 替换了 SomeView.get,所以当 SomeView.get 被调用时,它首先检查缓存,但在未命中时回退到未修饰的方法。

我希望这种方法能够在通用适用性和内容感知密钥派生之间取得平衡。