过滤部分确实很棘手。使用简单的技巧,您可以轻松地让泡菜发挥作用。但是,当过滤器看起来更深一点时,您最终可能会过滤掉太多并丢失您可以保留的信息。但是最终可能会出现的事情的巨大可能性.namespace
使得构建一个好的过滤器变得困难。
但是,我们可以利用已经是 Python 一部分的部分,例如模块deepcopy
中的部分。copy
我复制了 stockcopy
模块,做了以下事情:
- 创建一个名为的新类型
LostObject
来表示将在酸洗中丢失的对象。
- 更改
_deepcopy_atomic
以确保x
可腌制。如果不是,则返回一个实例LostObject
- 对象可以定义方法
__reduce__
和/或__reduce_ex__
提供有关是否以及如何腌制它的提示。我们确保这些方法不会抛出异常以提供无法腌制的提示。
- 为了避免对大对象进行不必要的复制(实际的 deepcopy),我们递归地检查对象是否是可腌制的,并且只制作不可腌制的部分。例如,对于一个可挑选列表和不可挑选对象的元组,我们将制作一个元组的副本——只是容器——而不是它的成员列表。
以下是差异:
[~/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)
+ # if x is picklable, there is no need to make a new copy, just ref it
+ 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))
# now x contains a loop and a unpickable file object
# the following line will throw
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
# though x has a big List object, this deepcopy will not make a new copy of it
c = deepcopy(x)
dumps(c)
cc = loads(dumps(c))
# check loop refrence
if cc['xx']['x'] == cc:
print 'yes, loop reference is preserved'
# check unpickable part
if isinstance(cc['f'], LostObject):
print 'unpicklable part is now an instance of LostObject'
# check large object
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) unpicklable 文件对象被转换为LostObject
实例;3) 不会创建大对象的新副本,因为它是可腌制的。