如果我使用派生类,我可以“pickle local objects”?

时间:2016-05-03 02:56:22

标签: python python-3.x nested pickle

PosVol = [ 0.0437 0.1680 0.2089 0.5719 0.9146 0.8306 0.8646 1.0000 0.6541 0.1628 -0.3038 -0.0990 0.3602 0.4707 0.2968 0.1793]; NegVol = [ 1.2838 1.6351 1.5026 1.1441 1.2331 1.2732 0.9188 0.1841 0.0412 0.6215 1.1444 1.0828 0.5687 0.2590 0.3917 0.5605]; PosInd = [ 1533 4057 6609 9038 11426 13831 16403 19009 21206 23609 27015 29758 32294 34789 37147 39642]; NegInd = [ 2304 4733 7283 9768 12291 14817 17397 20105 22899 25851 28218 30706 33317 36211 38628 41123]; 引用states that可以被腌制的对象集相当有限。实际上,我有一个返回一个dinamically生成的类的函数,我发现我不能挑选该类的实例:

pickle

此类对象对于>>> import pickle >>> def f(): ... class A: pass ... return A ... >>> LocalA = f() >>> la = LocalA() >>> with open('testing.pickle', 'wb') as f: ... pickle.dump(la, f, pickle.HIGHEST_PROTOCOL) ... Traceback (most recent call last): File "<stdin>", line 2, in <module> AttributeError: Can't pickle local object 'f.<locals>.A' 而言过于复杂。好。现在,有什么神奇之处在于,如果我试图挑选一个类似的对象,而不是衍生类,它就可以了!

pickle

这里发生了什么?如果这很容易,为什么不>>> class DerivedA(LocalA): pass ... >>> da = DerivedA() >>> with open('testing.pickle', 'wb') as f: ... pickle.dump(da, f, pickle.HIGHEST_PROTOCOL) ... >>> 使用此变通方法来实现允许“本地对象”被腌制的pickle方法?

4 个答案:

答案 0 :(得分:27)

我认为您没有仔细阅读the reference you cite。该参考文献还明确指出只有以下对象是可选择的:

  
      
  • 在模块顶层定义的函数(使用def,而不是&gt; lambda)
  •   
  • 在模块顶层定义的内置函数
  •   
  • 模块顶层
  • 中定义的类   

你的例子

>>> def f():
...     class A: pass
...     return A

没有在模块的顶层定义一个类,它在f()范围中定义了一个类。 pickle适用于全局类,而非本地类。这会自动使pickleable测试失败。

DerivedA是一个全球类,所以一切都很好。

至于为什么只能对顶层(全局到你)的类和函数进行腌制,参考文献也会回答这个问题(大胆的):

  

请注意,函数(内置和用户定义)由“完全限定”名称引用进行选择,而不是按值进行选择。这意味着只有函数名称被腌制,以及定义函数的模块的名称。函数的代码或其任何函数属性都不会被腌制。因此,定义模块必须可以在unpickling环境中导入,并且模块必须包含命名对象,否则将引发异常。

     

类似地,类通过命名引用进行pickle,因此适用于unpickling环境中的相同限制。

所以你有它。 pickle仅按名称引用序列化对象,而不是按对象中包含的原始指令序列化。这是因为pickle's作业是序列化对象层次结构,而不是其他任何内容。

答案 1 :(得分:17)

我不同意,你可以腌制两者。您只需要使用更好的序列化程序,例如dilldill(默认情况下)通过保存类定义来修改类,而不是通过引用进行pickling,因此它不会使您的第一个案例失败。如果愿意,您甚至可以使用dill来获取源代码。

>>> import dill as pickle
>>> def f():
...   class A: pass
...   return A
... 
>>> localA = f()
>>> la = localA()
>>> 
>>> _la = pickle.dumps(la)
>>> la_ = pickle.loads(_la)
>>>    
>>> class DerivedA(localA): pass
... 
>>> da = DerivedA()
>>> _da = pickle.dumps(da)
>>> da_ = pickle.loads(_da)
>>> 
>>> print(pickle.source.getsource(la_.__class__))
  class A: pass

>>> 

答案 2 :(得分:2)

DerivedA个实例是pickleable,因为DerivedA可通过匹配其完全限定名称的全局变量获得,这就是pickle在取消排版时查找类的方式。

尝试使用本地类执行此类操作的问题在于,没有任何标识实例所对应的 A类。如果你运行f两次,你会得到两个A类,并且没有办法告诉哪一个应该是来自另一个程序运行的未标记A实例的类。如果您根本没有运行f,那么您将获得 no A类,然后您对未修改的实例的类型做了什么?

答案 3 :(得分:2)

您只能腌制在模块顶层定义的类的实例。

但是,如果将本地定义的类的实例提升为顶级,则可以使它们腌制。

您必须设置本地类的 __ qualname __ 类属性。然后,您必须将该类分配给同名的顶级变量。

def define_class(name):
    class local_class:
        pass
    local_class.__qualname__ = name
    return local_class

class_A = define_class('class_A') # picklable
class_B = define_class('class_B') # picklable
class_X = define_class('class_Y') # unpicklable, names don't match