这个问题并不完全是 App Engine 特定的,但它可能有助于了解上下文:我在 App Engine 上有一种“静态站点生成器”,它可以呈现页面并允许它们通过各种主题和主题设置进行样式设置。主题当前直接存储在 App Engine 文件系统中并随应用程序一起上传。一个主题由几个模板和 yaml 配置数据组成。
为了封装使用主题,我有一Theme
堂课。theme = Theme('sunshine')
例如,构造一个 Theme 实例来加载和解析名为“sunshine”的主题的配置数据,并允许这样的调用theme.render_template('index.html')
自动加载并在文件系统上呈现正确的文件。
问题是,每次有新请求进入并实例化 a 时,加载并特别是解析主题的(yaml)配置数据Theme
是昂贵的。所以,我想在进程/App Engine 实例中缓存数据,也许稍后在 memcached 中。
到目前为止,我一直使用非常简单的缓存,如下所示:
class Theme(object):
_theme_variables_cache = {}
def __init__(self, name):
self.name = name
if name not in Theme._theme_variables_cache:
Theme._theme_variables[name] = self.load_theme_variables()
...
(我知道当多个请求同时命中构造函数时,可能会多次读取配置。但我认为这不会导致问题。)
但是这种缓存很快就会变得丑陋。我想从配置文件中读取几个不同的内容,所有缓存都是字典,因为每个不同的主题“名称”也指向不同的底层配置。
我的最后一个想法是创建一个这样的函数Theme._cached_func(func)
,仅当函数结果尚未为特定模板缓存时才执行 func(请记住,当对象表示不同的模板时,缓存的值也可能不同)。所以我可以像这样使用它:self.theme_variables = Theme._cached_func(self.load_theme_variables())
,但是,我觉得我在这里遗漏了一些明显的东西,因为我对 Python 还是很陌生。
是否有一个明显而干净的 Python 缓存模式可以在这种情况下工作,而不会用缓存逻辑混淆整个类?我想我不能只通过装饰器或其他东西来记忆函数结果,因为不同的模板必须有不同的缓存。我什至不需要任何“陈旧”缓存处理,因为底层配置数据在进程运行时不会改变。
更新
我最终这样做了:
class ThemeConfig(object):
__instances_cache = {}
@classmethod
def get_for(cls, theme_name):
return cls.__instances_cache.setdefault(
theme_name, ThemeConfig(theme_name))
def __init__(self, theme_name):
self.theme_name = theme_name
self._load_assets_urls() # those calls load yaml files
self._load_variables()
...
class Theme(object):
def __init__(self, theme_name):
self.theme_name = theme_name
self.config = ThemeConfig.get_for(theme_name)
...
因此,ThemeConfig
存储从文件系统中读取的主题的所有配置内容,并且工厂方法ThemeConfig.get_for
将始终为相同的主题名称分发相同的 ThemeConfig 实例。我唯一的缓存逻辑是工厂方法中的一行,Theme
对象仍然像往常一样是临时的和非共享的,所以我可以随意使用和滥用它们。