Python mixin / decorator / __ metaclass__ для улучшения базового класса - PullRequest
0 голосов
/ 10 сентября 2018

Я внедряю систему кэширования с учетом содержимого для API-интерфейса REST Django.Я хотел бы разработать компонент, который может быть добавлен к существующим представлениям, который будет изменять поведение базового класса, проверяя кэш и возвращаясь к поведению базового класса при промахе.

В принципе, у меня есть кое-чтокак это:

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

Так что ясно, что это не работает, так как я не знаю, как, илиесли это возможно, или если желательно получить доступ к суперклассу (ам) брата из миксина.

Моя цель - реализация, которая позволяет мне добавлять поведение кэширования к существующим представлениям, не касаясь внутренних элементов существующих классов.Для заданного класса представления C, st C.get(request, *args, **kwargs) -> Response, есть ли функция, F, st F(C).get(..., проверяет ли кэш, прежде чем вернуться к C.get?И в этой квазиформальной нотации мы скажем, что добавление миксина к крайнему левому родительскому классу в определении класса считается функцией.

Правильнее ли использовать методы декораторов?или как будет работать декоратор класса?

А потом я видел ссылки на __metaclass__ при исследовании этого, но мне не ясно, как выглядит этот подход.

Это Python 3.6

Ответы [ 3 ]

0 голосов
/ 11 сентября 2018

Простой пример:

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 там перезаписывается.

Для этой проблемы вам не нужны метаклассыили декораторы, если вы хотите просто добавить кэширование в одном месте.

Конечно, декоратор может использоваться для включения кэширования еще более общим способом.

См., например, этот ответ: Существует ли декоратор для простого кэширования возвращаемых значений функции?

0 голосов
/ 18 сентября 2018

Ответом был декоратор и некоторые 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, к method_decorator.Я определяю функцию высшего порядка, cached_get, которая принимает другую функцию в качестве аргумента и возвращает карри функцию (так называемое замыкание).Вызывая это, с помощью функции get_cache_key (и не имейте в виду, , вызывая эту функцию), у меня есть декоратор, который будет применен к методу get в SomeView.

Сам декоратор является простым декоратором Python - в этом приложении это cached_func, а оригинальный, неокрашенный get метод - func.Таким образом, cached_func заменяет SomeView.get, поэтому, когда вызывается SomeView.get, он сначала проверяет кэш, но возвращается к неокрашенному методу при промахе.

Я надеюсь, что этот подход обеспечивает баланс универсальной применимости с выводом ключа с учетом содержимого.

0 голосов
/ 11 сентября 2018

Мои два цента:

  1. Вы идете на неясную территорию здесь. Познакомьтесь со всеми связанными понятиями, попробуйте несколько, а затем решите.
  2. Здесь - хорошее руководство по метаклассам.
  3. Здесь есть один из декораторов.
  4. Я никоим образом не связан с этим сайтом.
...