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
我的两分钱:
简单示例:
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
被调用时,它首先检查缓存,但在未命中时回退到未修饰的方法。
我希望这种方法能够在通用适用性和内容感知密钥派生之间取得平衡。
我正在为 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
我的两分钱:
简单示例:
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
被调用时,它首先检查缓存,但在未命中时回退到未修饰的方法。
我希望这种方法能够在通用适用性和内容感知密钥派生之间取得平衡。