2012-10-24 135 views
3

我嘗試了一些代碼,但它似乎會導致問題:Python對象緩存

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' 

哪些錯誤與此位的代碼?爲什麼不行?有沒有辦法讓它工作?如果不是,我如何輕鬆,透明地向最終用戶緩存對象?

回答

5

,如果你要處理的創建,您需要更改__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 
>>> 
+0

這很聰明,只是不使用'__init__' .. –

+0

@BrendanLong,如果你有辦法找出什麼時候跳過它,你可以使用'__init__'。 – dnozay

0

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 
+0

有沒有一種方法,使工作?如果不是,我如何輕鬆,透明地向最終用戶緩存對象? – Justin808

+0

而不是設置自我,嘗試'返回o',但是然後我得到python大喊__init__應該返回None – Justin808

+0

@ Justin808顯然不是。我正在使用'__new__',但顯然這是行不通的(參見poke的回答)。 –

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)