2013-01-24 54 views
1

下面的代碼運行正常:是否可以修飾其實例將被酸洗的Python類?

import pickle 

class MyClass(): 
    def __init__(self, arg): 
     self.arg = arg 

a = MyClass('my arg') 
with open('/home/mahikeulbody/mypickle', 'wb') as file: 
    pickle.dump(a, file) 

,但增加了裝飾以獲得多例類:

import pickle 

def multiton(cls): 
    instances = {} 
    def getinstance(arg): 
     if arg not in instances: 
      instances[arg] = cls(arg) 
     return instances[arg] 
    return getinstance 

@multiton 
class MyClass(): 
    def __init__(self, arg): 
     self.arg = arg 

a = MyClass('my arg') 
with open('/home/michel/mypickle', 'wb') as file: 
    pickle.dump(a, file) 

產生以下錯誤:

pickle.dump(a, file) 
_pickle.PicklingError: Can't pickle <class '__main__.MyClass'>: it's not the same object as __main__.MyClass 

有什麼不對?

+1

呃,這個裝飾者比那個更有問題。這不僅僅是一個班級,它已經成爲一個工廠職能。例如,您不能使用它進行檢查或對其進行子類化。 – delnan

回答

2

Pickle必須能夠直接加載類。你的裝飾器用工廠函數替換了這個類,這使得pickle不可能導入類本身。

使用一個單獨的工廠函數,而不是裝飾,返回一個「私人」類(但仍然可導入直接):

class _MyClass(): 
    def __init__(self, arg): 
     self.arg = arg 

def MyClass(arg, instances={}): 
    if arg not in instances: 
     instances[arg] = _MyClass(arg) 
    return instances[arg] 
+0

答案的一個副作用是,我驚奇地發現,變量實例在函數的不同調用中是持久的。這讓我感到有些震驚...... – mahikeulbody

+0

@ user2008329:請參閱[「Python中的最小驚訝」:可變的默認參數](http://stackoverflow.com/q/1132941)。當你意識到功能如何工作時,這是完全合理的。 :-) –

0

要做到這一點,我會用dill,可序列化幾乎所有的東西蟒蛇。

>>> def multiton(cls): 
...  instances = {} 
...  def getinstance(arg): 
...   if arg not in instances: 
...    instances[arg] = cls(arg) 
...   return instances[arg] 
...  return getinstance 
... 
>>> @multiton 
... class MyClass(): 
...  def __init__(self, arg): 
...   self.arg = arg 
... 
>>> import dill 
>>>      
>>> a = MyClass('my arg') 
>>> b = dill.loads(dill.dumps(a)) 
>>> a 
<__main__.MyClass instance at 0x4d64558> 
>>> b 
<__main__.MyClass instance at 0x4d64800> 

蘿也有some good tools幫助您瞭解是什麼原因導致你的酸洗當你的代碼沒有失敗。