我试图实现一个使用磁盘上的pickle作为持久存储的持久性字典的(原型,而非生产版本)。但是,pickle.load
出于其自身目的调用__setitem__
,这是(当然)被覆盖以确保将字典更改传播回持久性存储的方法 - 因此它调用pickle.dump
。当然,因为在取出期间每个项目都被设置,因此拨打pickle.dump
并不好。如何处理调用`__setitem__`的pickle.load`还没有准备好?
有没有什么办法可以解决这个问题,除了通过蛮力(如下)?我试着读Pickling Class Instances寻找一种使用特殊方法的解决方案,但没有找到任何解决方案。
下面的代码监视unpickling是否正在进行,并在这种情况下跳过pickle.dump
;虽然它工作正常,但感觉很不舒服。
import os, pickle
class PersistentDict(dict):
def __new__(cls, *args, **kwargs):
if not args: # when unpickling
obj = dict.__new__(cls)
obj.uninitialized = True
return obj
path, *args = args
if os.path.exists(path):
obj = pickle.load(open(path, 'rb'))
del obj.uninitialized
return obj
else:
obj = dict.__new__(cls, *args, **kwargs)
obj.path = path
obj.dump()
return obj
def __init__(self, *args, **kwargs):
pass
def __setitem__(self, key, value):
super().__setitem__(key, value)
self.dump()
def __delitem__(self, key):
super().__delitem__(key)
self.dump()
def dump(self):
if not hasattr(self, 'uninitialized'):
pickle.dump(self, open(self.path, 'wb'))
def clear(self):
os.remove(self.path)
pd = PersistentDict('abc')
assert pd == {}
pd[1] = 2
assert pd == {1: 2}
pd[2] = 4
assert pd == {1: 2, 2: 4}
del pd[1]
assert pd == {2: 4}
xd = PersistentDict('abc')
assert xd == {2: 4}
xd[3] = 6
assert xd == {2: 4, 3: 6}
yd = PersistentDict('abc')
assert yd == {2: 4, 3: 6}
yd.clear()
给你的类指定一个字典属性并将数据存储在那里可能比较容易,而不是让你的类从字典继承。然后,你可以腌制存储的字典,而不是你的PersistentDict,分离两层。 – BrenBarn
@BrenBarn这正是我的想法,但我非常偏向于继承的开始,直到我总是用组合替换它。所以这一次,我想继承一下。我知道唯一支持继承的论点是,使用'__getattr__'的自动转发不会转发特殊的方法(比如'__getitem__','__contains__','__eq__'等等),而且转发有点麻烦他们全部手动。但这似乎最终成为继承处理比构图更令人沮丧的另一个例子。 – max