15

Python pickle:处理更新的类定义的答案中,包的作者dill写道:

“好的,我已经在 github 上的最新版本中添加了这个功能。实现起来比我想象的要少得多……只需用 pickle 序列化类定义,瞧。”

安装dill并修改它后,对我来说如何在dill. 有人可以提供一个明确的例子吗?我想腌制类实例并序列化类定义。

(我是 python 新手,我这个功能似乎非常重要,因为当腌制一个对象时,尽可能接近保证你可以看到对象(可能是模拟的结果)会很棒类定义之后的未来可能已经改变,并且您没有以易于访问的方式跟踪所有更改。)

4

2 回答 2

12

我认为您正在寻找以下功能之一……</p>

这里我建立了一个类和一个实例,然后更改了类定义。腌制的类和实例仍然是不可腌制的,因为dill默认情况下腌制类的源代码......并管理在命名空间中具有多个具有相同名称的类(它只是通过管理对类定义的指针引用来做到这一点)。

Python 2.7.8 (default, Jul 13 2014, 02:29:54) 
[GCC 4.2.1 Compatible Apple Clang 4.1 ((tags/Apple/clang-421.11.66))] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> 
>>> class Foo(object):
...   def bar(self, x):
...     return x+self.y       
...   y = 1
... 
>>> f = Foo()
>>> _Foo = dill.dumps(Foo)
>>> _f = dill.dumps(f)
>>> 
>>> class Foo(object):
...   def bar(self, x):
...     return x*self.z  
...   z = -1 
... 
>>> f_ = dill.loads(_f, ignore=True)
>>> f_.y
1
>>> f_.bar(1)
2
>>> Foo_ = dill.loads(_Foo)
>>> g = Foo_()
>>> g.bar(1)
2

泡菜会在上面炸毁。如果您不想dill显式序列化该类,并且不想执行该pickle操作,那么您可以dill通过引用来请求 pickle dill.dumps(Foo, byref=True)ignore=False或者,您可以通过使用(默认)动态决定忽略新定义的类。

现在,在下面的例子中,我们使用新的类定义,并从对象中提取源代码,然后将其保存到文件中。此外,我们可以将源文件转储到一个文件中(这里我使用一个临时文件),以便以后可以导入。

>>> sFoo = dill.source.getsource(Foo)
>>> print sFoo
class Foo(object):
  def bar(self, x):
    return x*self.z
  z = -1

>>> open('myFoo.py', 'w').write(sFoo)    
>>>
>>> f = dill.temp.dump_source(Foo, dir='.')
>>> f.name
'/Users/mmckerns/dev/tmpM1dzYN.py'
>>> from tmpM1dzYN import Foo as _Foo_
>>> h = _Foo_()
>>> h.bar(2)
-2
>>> from myFoo import Foo as _SFoo_
>>> _SFoo_.z
>>> -1
>>> 

我希望这会有所帮助。

于 2014-09-02T12:32:06.043 回答
5

如果这个功能那么重要,那么它现在应该在语言核心中。:-) 所以,不,这对于以任何高级形式使用 Python 并不重要——如果你的项目依赖于能够基于旧模型重新实例化对象——这是可能的,你必须仔细考虑它,并且可能将旧模型保留在显式代码中,而不是随后序列化。

我的建议只是“将其分开”,直到您认为您确实需要它,并将其与其他解决方案(例如强大的模型迁移策略)进行了比较。

也就是说,我尝试过 dill,它的工作原理与宣传的一样:它可以序列化一个类,就像 pickle 可以使用“dump”和“dumps”调用对普通对象所做的那样,并使用“load”和“重建类对象”负载”。

可能让您感到困惑的是,序列化对象(通过 pickle 或 dill 之类的方法)既不包括其源代码(即用于定义类的文本 Python 代码的实际行),也不包括其名称。

因此,如果一个类被命名为“A”,当它被序列化时,如果在“取消”它之后需要该名称,则必须在全局名称空间中重新分配该名称。它的原始名称保留在它的__name__属性中。(并且出于您将同一模型的多个版本一起生活的目的,这会导致很多冲突)。

因此:

class A(object):
    ...

import dill

dill.dump(A, open("myfile", "w"))

del A
....
someclass = dill.load(open("myfile"))
print (someclass.__name__)
globals()[someclass.__name__] = someclass
# at this point you have the "A" class back in the global namespace
于 2014-09-02T11:47:50.023 回答