过滤部分确实很棘手。使用简单的技巧,您可以轻松地让pickle工作。然而,当过滤器深入一点时,您可能会过滤掉太多内容并且失去了本来可以保留的信息。但是,可能出现在.namespace中的各种可能性使得构建一个好的过滤器变得困难。
然而,我们可以利用已经存在于Python中的部分内容,例如copy模块中的deepcopy。
我复制了原始的copy模块,并进行了以下操作:
- 创建一个名为
LostObject
的新类型,用于表示在pickling过程中将丢失的对象。
- 更改
_deepcopy_atomic
以确保x
是可picklable的。如果不是,则返回LostObject
的实例。
- 对象可以定义方法
__reduce__
和/或__reduce_ex__
来提供关于是否以及如何pickle它的提示。我们确保这些方法不会抛出异常,以提供无法pickle的提示。
- 为了避免不必要地复制大型对象(类似于实际的深度复制),我们递归检查对象是否可pickle,并仅制作不可pickle的部分。例如,对于包含可pickle列表和不可pickle对象的元组,我们将复制元组 - 仅容器 - 而不是其成员列表。
以下是差异:
[~/Development/scratch/] $ diff -uN /System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/copy.py mcopy.py
--- /System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/copy.py 2010-01-09 00:18:38.000000000 -0800
+++ mcopy.py 2010-11-10 08:50:26.000000000 -0800
@@ -157,6 +157,13 @@
cls = type(x)
+
+ try:
+ dumps(x)
+ return x
+ except TypeError:
+ pass
+
copier = _deepcopy_dispatch.get(cls)
if copier:
y = copier(x, memo)
@@ -179,10 +186,18 @@
reductor = getattr(x, "__reduce_ex__", None)
if reductor:
rv = reductor(2)
+ try:
+ x.__reduce_ex__()
+ except TypeError:
+ rv = LostObject, tuple()
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
+ try:
+ x.__reduce__()
+ except TypeError:
+ rv = LostObject, tuple()
else:
raise Error(
"un(deep)copyable object of type %s" % cls)
@@ -194,7 +209,12 @@
_deepcopy_dispatch = d = {}
+from pickle import dumps
+class LostObject(object): pass
def _deepcopy_atomic(x, memo):
+ try:
+ dumps(x)
+ except TypeError: return LostObject()
return x
d[type(None)] = _deepcopy_atomic
d[type(Ellipsis)] = _deepcopy_atomic
现在回到腌制部分。您只需使用这个新的
deepcopy
函数进行深拷贝,然后将副本进行腌制。在复制过程中,无法腌制的部分已被删除。
x = dict(a=1)
xx = dict(x=x)
x['xx'] = xx
x['f'] = file('/tmp/1', 'w')
class List():
def __init__(self, *args, **kwargs):
print 'making a copy of a list'
self.data = list(*args, **kwargs)
x['large'] = List(range(1000))
from pickle import dumps, loads
try:
dumps(x)
except TypeError:
print 'yes, it throws'
def check_picklable(x):
try:
dumps(x)
except TypeError:
return False
return True
class LostObject(object): pass
from mcopy import deepcopy
c = deepcopy(x)
dumps(c)
cc = loads(dumps(c))
if cc['xx']['x'] == cc:
print 'yes, loop reference is preserved'
if isinstance(cc['f'], LostObject):
print 'unpicklable part is now an instance of LostObject'
if loads(dumps(c))['large'].data[999] == x['large'].data[999]:
print 'large object is ok'
这是输出结果:
making a copy of a list
yes, it throws
yes, loop reference is preserved
unpicklable part is now an instance of LostObject
large object is ok
你会发现:1)互相指针(即 x
和 xx
之间的指针)得到了保留且不会进入无限循环;2)无法反序列化的文件对象被转换成了LostObject
实例;3)由于该大型对象是可序列化的,因此不会创建新的副本。
Pickler
类?如果可以,我还需要子类化Unpickler
吗?它们如何知道如何协同工作,例如,如果有人尝试使用标准的loads
来反序列化由我的Pickler
子类pickle的内容,会发生什么? - Ram RachumGuiProject.__getstate__
函数内部,是否有可能找出正在对我们进行 pickling 的Pickler
子类,以便断言它是我们的特殊 pickler? - Ram RachumPickler
和Unpickler
而不是继承它们?继承它们有什么问题吗? - Ram Rachum