2013-01-24 30 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帮助您了解是什么原因导致你的酸洗当你的代码没有失败。