Python mixin / decorator / __ metaclass__用于增强基类

时间:2018-09-10 18:25:23

标签: python django metaprogramming multiple-inheritance python-decorators

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

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

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

很显然,这不起作用,因为我不知道如何,是否可能,或者是否建议从mixin访问同级超类。

我的目标是一个实现,使我可以在不影响现有类内部的情况下向现有视图添加缓存行为。 给定一个视图类C,s.t。 C.get(request, *args, **kwargs) -> Response,有一个功能,FF(C).get(...在回退到C.get之前进行缓存检查吗?在这种准形式表示中,我们将说在类定义中最左边的父类中添加一个mixin算作一个函数。

使用方法修饰符是否更合适?或类装饰器如何工作?

然后我在研究此内容时看到了对__metaclass__的引用,但是我不清楚该方法的外观。

这是Python 3.6

3 个答案:

答案 0 :(得分:1)

简单的例子:

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?

答案 1 :(得分:0)

答案是装饰器和一些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,将其第一个参数(装饰器)应用于以第二个参数name命名的装饰类的方法到{{1} }。我定义了一个高阶函数method_decorator,该函数将另一个函数作为参数,并返回一个curried函数(闭包,即所谓的闭包)。通过使用函数cached_get(而不是,请注意,调用该函数)调用此函数,我得到了一个装饰器,该装饰器将应用于get_cache_key上的'get'方法。

修饰器本身是一个简单的Python装饰器-在此应用程序中为SomeView,原始的未经修饰的cached_func方法为get。因此,func替换了cached_func,因此,在调用SomeView.get时,它首先检查高速缓存,但是在未命中时退回到未经修饰的方法。

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

答案 2 :(得分:-2)

我的两分钱

  1. 您正在这里进入晦涩的地方。熟悉所有相关概念,尝试一些,然后再决定。
  2. Here是有关元类的很好的教程。
  3. Here关于装饰器。
  4. 我绝不隶属于该网站。