如何使用dill序列化类定义?
在回答Python pickle: 处理更新的类定义这个问题时,dill
包的作者写道:
"好的,我在github的最新版本中为dill添加了这个功能。实现起来比我想象的简单得多……只需将类定义与pickle一起序列化,瞧!"
我安装了dill
并试着玩了一下,但我还是不太明白怎么用这个功能。有人能给个明确的例子吗?我想把类的实例和类的定义都序列化。
(我刚接触python,这个功能对我来说似乎非常重要,因为在对一个对象进行序列化时,能够尽可能保证将来查看这个对象(可能是某个模拟的结果)时,即使类的定义发生了变化,而我又没有记录所有变化,这样也能方便地访问。)
2 个回答
如果这个功能真的那么重要,现在早就应该在语言的核心部分了。:-) 所以,不,这对使用Python的高级功能并不是关键。如果你有一个项目需要根据旧模型重新创建对象,这确实是可能的,但你得仔细考虑一下,可能还得在代码中明确保留旧模型,而不是把它们序列化。
我的建议是,先“放一放”这个问题,直到你真的觉得需要它,并且和其他解决方案,比如强有力的模型迁移策略,进行比较。
话说回来,我试过dill,它的确如宣传的那样好用:它可以像pickle一样序列化一个类,使用“dump”和“dumps”来处理普通对象,然后用“load”和“loads”来重建类对象。
让你感到困惑的可能是,序列化一个对象(通过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
我想你可能在寻找以下几种功能……
这里我创建了一个类和一个实例,然后又修改了这个类的定义。使用 dill
序列化的类和实例仍然无法被反序列化,因为 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
如果用 pickle
来处理上面的情况,会出错。如果你不想让 dill
显式地序列化这个类,而是想让它像 pickle
那样工作,你可以用 dill.dumps(Foo, byref=True)
来请求 dill
通过引用进行序列化。或者,你也可以选择动态地忽略新定义的类,使用 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
>>>
希望这些对你有帮助。