用于基类增强的Python mixin/decorator/__元类
我正在为Django REST API实现一个内容感知缓存系统。我想开发一个可以添加到现有视图中的组件,该组件将通过检查缓存并返回到未命中时的基类行为来修改基类的行为 基本上,我有这样的想法:用于基类增强的Python mixin/decorator/__元类,python,django,metaprogramming,multiple-inheritance,python-decorators,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)
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
,是否有函数,F
,s.t.F(C).get(…
在返回到C.get
之前进行缓存检查?在这种准形式表示法中,我们将说向类定义中最左边的父类添加mixin算作一个函数
使用方法装饰器更合适吗?或者类装饰器是如何工作的
然后我在研究中看到了对\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
的引用,但我不清楚这种方法是什么样子的
这是Python 3.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继承,您可以在此处包含缓存,因为\u get\u data
在此处被覆盖
对于这个问题,如果只想在一个位置添加缓存,则不需要元类或装饰器
当然,可以使用装饰器以更通用的方式包含缓存
例如,请参见此答案:答案是一个装饰器和一些特定于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的内置方法\u decorator
,它将其第一个参数decorator应用于被修饰类的方法,由第二个参数name
命名为方法\u decorator
返回一个curried函数(闭包,所谓的)。通过调用此函数,使用函数get\u cache\u key
(请注意,不是调用该函数),我有一个装饰器,将应用于SomeView
上的“get”方法
decorator本身是一个简单的Python decorator——在这个应用程序中,它是cached_func
,原始的、未修饰的get
方法是func
。因此,cached_func
替换SomeView.get
,因此当调用SomeView.get
时,它首先检查缓存,但返回到未经修饰的方法
我希望这种方法能够在通用适用性和内容感知密钥派生之间取得平衡。我认为部分问题也在于
cache.get(cache\u key,base\u get(request,*args,**kwargs))
。你不必要地计算基本获取。这是一个不错的评论,但我认为作为回答,它只是一个链接。你唯一真正的内容是“玩它”。事实上,我对所有的概念都很熟悉,我目前正在尝试各种选择。我来到这里是因为其他人可能有见解,并且因为描述问题以便其他人能够理解是发展我自己理解的最有效策略。我将在评论中详细阐述,但您提供的示例代码de不处理我的设计并非所有视图都继承自同一基类,但可以预期所有视图都有一个公共方法,get(self、request、*args、**kwargs)
当然,它们是这样的:都是从object继承的。我只是想提供一个例子,说明如何实现,而不是给您一个完整的解决方案。完整的解决方案必须由您在完整程序的上下文中定义。另外:您不需要有相同的基类。只需定义可以覆盖的正确方法。如果所有这些如果不符合您的问题,您可以查看一下decorator解决方案(我提供了上面的链接)。我不认为您需要元类来解决此类问题。我了解如何使用简单继承来实现这一点。我要寻找的是一种不需要更改现有类内部结构的技术。此方法需要重构所有现有视图。如果没有changi,则无法添加新功能在任何情况下都要重新定义类。你可以看看decorator技术。如果它起作用,你是幸运的。如果不起作用,你可能需要进行一些重构。以一种可以轻松添加附加功能的方式构造类属于架构远见。没有“银弹”这神奇地为您的设计添加了无条件的内容。这就是为什么架构和类设计需要很多远见和经验的原因。