在 Python 类中缓存数据(以避免在 App Engine 上读取昂贵的文件系统)

Car*_*t3n 5 python google-app-engine

这个问题并不完全是 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()

...
Run Code Online (Sandbox Code Playgroud)

(我知道当多个请求同时命中构造函数时,可以多次读取配置。不过我认为这不会导致问题。)

但是这种缓存很快就会变得丑陋。我想从配置文件中读取几种不同的内容,并且所有缓存都是字典,因为每个不同的主题“名称”也指向不同的底层配置。

我的最后一个想法是创建一个这样的函数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)
...
Run Code Online (Sandbox Code Playgroud)

因此,ThemeConfig存储从文件系统中读取的主题的所有配置内容,并且工厂方法ThemeConfig.get_for将始终为相同的主题名称分发相同的 ThemeConfig 实例。我拥有的唯一缓存逻辑是工厂方法中的一行,并且Theme对象仍然像往常一样临时和非共享,因此我可以随意使用和滥用它们。

Pra*_*nde 3

我会尝试一下。基本上,这里可以使用工厂模式来维护Theme对象和以特定方式创建Theme实例之间的清晰边界。

工厂本身还可以通过存储主题名称和相应主题对象之间的映射来维护简单的缓存策略。我会采用以下实现:

#the ThemeFactory class instantiates a Theme with a particular name if not present within it's cache
class ThemeFactory(object) :

     def __init__(self):
         self.__theme_variables_cache = {}

     def createTheme(self, theme_name):
         if not self.__theme_variables_cache.contains(name):
              theme = Theme(theme_name)
              self.__theme_variables_cache[name] = theme.load_theme_variables()
          return self.__theme_variables_cache[name]
Run Code Online (Sandbox Code Playgroud)

Theme类的定义现在非常干净和简单,并且不会包含任何缓存复杂性

class Theme(object):

    def __init__(self, name):
        self.__theme_name = name

    def load_theme_variables(self):
        #contain the logic for loading theme variables from theme files
Run Code Online (Sandbox Code Playgroud)

该方法具有代码可维护性和职责明确分离的优点(尽管不完全如此,工厂类仍然维护简单的缓存。理想情况下,它应该简单地引用缓存服务或处理缓存的另一个类..但是你得到点)。

您的Theme类做了它最擅长的事情 - 加载主题变量。由于您有工厂模式,因此您可以将客户端代码(使用Theme类实例的代码)从创建Theme实例的逻辑中封装出来。随着应用程序的增长,您可以扩展此工厂来控制各种Theme对象的创建(包括从Theme派生的类)

请注意,这只是实现简单缓存行为以及实例创建封装的一种方法。

还有一点 - 您可以在缓存中存储主题对象而不是主题变量。这样,您只能在第一次使用时从模板中读取主题变量(延迟加载)。但是,在这种情况下,您需要确保将主题变量存储为Theme类的实例变量。该方法load_theme_variables(self)现在需要这样编写:

def load_theme_variables(self):
   #let the theme variables be stored in an instance variable __theme_variable
   if not self.__theme_variables is None:
       return self.__theme_variables
    #__read_theme_file is a private function that reads the theme files
   self__theme_variables = self.__read_theme_file(self.__theme_name)
Run Code Online (Sandbox Code Playgroud)

希望这能让您了解如何实现您的用例。