为了账号安全,请及时绑定邮箱和手机立即绑定

Python mixin/decorator/__metaclass__ 用于基类增强

Python mixin/decorator/__metaclass__ 用于基类增强

回首忆惘然 2021-06-10 13:08:40
我正在为 Django REST API 实现一个内容感知缓存系统。我想开发一个可以添加到现有视图的组件,该组件将通过检查缓存并在未命中时回退到基类行为来修改基类的行为。基本上,我有这样的事情:class Base:   def get(self, request, *args, **kwargs):       ....       return Responseclass AnotherBase:   def get(self, request, *args, **kwargs):       ....        return Responseclass Derived(Base):    passclass 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 stuffclass Derived(Cacheable, Base):    passclass AnotherDerived(Cacheable, AnotherBase):    pass很明显这不起作用,因为我不知道如何,或者是否可能,或者是否建议从 mixin 访问兄弟超类。我的目标是一种实现,它允许我在不触及现有类的内部结构的情况下向现有视图添加缓存行为。给定一个视图类, C, st C.get(request, *args, **kwargs) -> Response,是否有一个函数, F, stF(C).get(...在回退到 之前是否进行缓存检查C.get?在这个准正式的表示法中,我们会说向类定义中最左边的父类添加一个 mixin 算作一个函数。使用方法装饰器是否更合适?或者类装饰器如何工作?然后我__metaclass__在研究这个时看到了参考资料,但我不清楚这种方法是什么样的。这是 Python 3.6
查看完整描述

3 回答

?
慕雪6442864

TA贡献1812条经验 获得超5个赞

简单的例子:


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被覆盖。


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


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


查看完整回答
反对 回复 2021-06-15
?
慕村225694

TA贡献1880条经验 获得超4个赞

答案是一个装饰器和一些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, to命名method_decorator。我定义了一个高阶函数,cached_get,它接受另一个函数作为它的参数,并返回一个柯里化函数(闭包,所谓的)。通过调用这个函数get_cache_key(而不是,请注意,调用该函数),我有一个装饰器,它将应用于 .get 方法SomeView。


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


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


查看完整回答
反对 回复 2021-06-15
?
潇湘沐

TA贡献1816条经验 获得超6个赞

我的两分钱:

  1. 你正在走进这里晦涩难懂的领域。熟悉所有相关概念,尝试一些,然后再决定。

  2. 是一个关于元类的很好的教程。

  3. 这里有一个关于装饰器。

  4. 我绝不隶属于该网站。


查看完整回答
反对 回复 2021-06-15
  • 3 回答
  • 0 关注
  • 110 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信