如何使用蒔蘿來序列化類定義?

2024-09-22 20:36:19 发布

您现在位置:Python中文网/ 问答频道 /正文

在对Python pickle: dealing with updated class definitions的回答中,dill包的作者写道:

“好的,我已经在github的最新版本中向dill添加了这个特性。用远比我想象的要少的诡计来实现。。。只要用pickle序列化类定义,就可以了。”

安装了dill并对其进行了修改之后,我不清楚如何在dill中实际使用此功能。有人能提供一个明确的例子吗?我希望pickle类实例并序列化类定义。

(我是python新手,这个功能看起来非常重要,因为当挑选一个对象时,最好尽可能接近一个保证,在类定义发生更改并且您没有跟踪一种容易接近的方式。)


Tags: 功能版本github序列化定义with作者特性
2条回答

我想您正在寻找以下功能之一

在这里,我构建一个类和一个实例,然后更改类定义。 pickled类和实例仍然是不可picklable的,因为dillpickles 默认情况下类的源代码…并管理有几个类 在命名空间中使用相同的名称(它只是通过管理指针 对类定义的引用)。

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通过引用dill.dumps(Foo, byref=True)来pickle。或者,可以使用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
>>> 

我希望这会有帮助。

如果这个功能如此重要的话,那么它现在就在语言核心中了。:-) 所以,不,在任何高级形式下使用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

相关问题 更多 >