【问题标题】:Python object cachePython 对象缓存
【发布时间】:2012-10-14 18:58:03
【问题描述】:

我尝试了一些代码,但似乎会导致问题:

class Page:
    cache = []


    """ Return cached object """
    def __getCache(self, title):
        for o in Page.cache:
            if o.__searchTerm == title or o.title == title:
                return o
        return None


    """ Initilize the class and start processing """
    def __init__(self, title, api=None):
        o = self.__getCache(title)
        if o:
            self = o
            return
        Page.cache.append(self)

        # Other init code
        self.__searchTerm = title
        self.title = self.someFunction(title)

那我试试:

a = Page('test')
b = Page('test')

print a.title # works
print b.title # AttributeError: Page instance has no attribute 'title'

这段代码有什么问题?为什么它不起作用?有没有办法让它工作?如果不是,我如何轻松、透明地处理最终用户缓存对象?

【问题讨论】:

    标签: python caching python-2.7 python-2.x


    【解决方案1】:

    如果你想操纵创作,你需要改变__new__

    >>> class Page(object):
    ...     cache = []
    ...     """ Return cached object """
    ...     @classmethod
    ...     def __getCache(cls, title):
    ...         for o in Page.cache:
    ...             if o.__searchTerm == title or o.title == title:
    ...                 return o
    ...         return None
    ...     """ Initilize the class and start processing """
    ...     def __new__(cls, title, api=None):
    ...         o = cls.__getCache(title)
    ...         if o:
    ...             return o
    ...         page = super(Page, cls).__new__(cls)
    ...         cls.cache.append(page)
    ...         page.title = title
    ...         page.api = api
    ...         page.__searchTerm = title
    ...         # ...etc
    ...         return page
    ... 
    >>> a = Page('test')
    >>> b = Page('test')
    >>> 
    >>> print a.title # works
    test
    >>> print b.title
    test
    >>> 
    >>> assert a is b
    >>> 
    

    编辑:使用__init__

    >>> class Page(object):
    ...     cache = []
    ...     @classmethod
    ...     def __getCache(cls, title):
    ...         """ Return cached object """
    ...         for o in Page.cache:
    ...             if o.__searchTerm == title or o.title == title:
    ...                 return o
    ...         return None
    ...     def __new__(cls, title, *args, **kwargs):
    ...         """ Initilize the class and start processing """
    ...         existing = cls.__getCache(title)
    ...         if existing:
    ...             return existing
    ...         page = super(Page, cls).__new__(cls)
    ...         return page
    ...     def __init__(self, title, api=None):
    ...         if self in self.cache:
    ...             return
    ...         self.cache.append(self)
    ...         self.title = title
    ...         self.api = api
    ...         self.__searchTerm = title
    ...         # ...etc
    ... 
    >>> 
    >>> a = Page('test')
    >>> b = Page('test')
    >>> 
    >>> print a.title # works
    test
    >>> print b.title
    test
    >>> assert a is b
    >>> assert a.cache is Page.cache
    >>> 
    

    【讨论】:

    • @BrendanLong,如果你想知道何时跳过它,你可以使用__init__
    【解决方案2】:

    一旦创建对象的实例,您就无法真正更改它。将self 设置为其他值时,您只需更改变量指向的引用,因此实际对象不受影响。

    这也解释了为什么title 属性不存在。更改本地 self 变量后立即返回,防止当前实例初始化 title 属性(更不用说此时 self 不会指向正确的实例)。

    所以基本上,您不能在初始化期间(在__init__)更改对象,因为此时它已经被创建并分配给变量。像a = Page('test') 这样的构造函数调用实际上是一样的:

    a = Page.__new__('test')
    a.__init__('test')
    

    如您所见,__new__ 类的构造函数首先被调用,而这实际上是由谁负责创建实例。所以你可以覆盖类的__new__ 方法来操作对象的创建。

    然而,通常首选的方法是创建一个简单的工厂方法,如下所示:

    @classmethod
    def create (cls, title, api = None):
        o = cls.__getCache(title)
        if o:
            return o
        return cls(title, api)
    

    【讨论】:

      【解决方案3】:

      self 是一个普通的局部变量,因此设置self = .. 只会改变self 变量在该函数中指向的内容。它不会改变实际的对象。

      见:Is it safe to replace a self object by another object of the same type in a method?

      要做你想做的事,你可以使用一个静态函数作为factory

      class Page:
          cache = []
      
      
          """ Return cached object """
          @staticmethod
          def create(title):
              for o in Page.cache:
                  if o.__searchTerm == title or o.title == title:
                      return o
              return Page(title)
      
          """ Initilize the class and start processing """
          def __init__(self, title, api=None):
              Page.cache.append(self)
      
              # Other init code
              self.__searchTerm = title
              self.title = title
      
      
      a = Page.create('test')
      b = Page.create('test')
      
      print a.title
      print b.title
      

      【讨论】:

      • 有没有办法让它工作?如果不是,我如何轻松、透明地处理最终用户缓存对象?
      • 与其设置self,而是尝试return o,但随后我被python大喊,init应该返回None
      • @Justin808 显然不是。我正在考虑使用__new__,但显然这行不通(请参阅戳的答案)。
      • 谢谢,我想我得去工厂了。我希望我可以对用户透明,但看起来 python 不允许这样做。
      • 有什么方法可以将__init__设为私有,这样用户调用的o = Page('test')会失败?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-06
      • 2018-05-27
      • 1970-01-01
      • 2012-10-07
      • 2012-08-04
      • 1970-01-01
      相关资源
      最近更新 更多