【发布时间】:2017-02-18 20:47:32
【问题描述】:
我试图实现一个(原型,而不是生产)版本的持久字典,它使用磁盘上的 pickle 作为持久存储。但是,pickle.load 出于自己的目的调用__setitem__,并且该方法(当然)被覆盖以确保对字典的更改传播回持久存储——因此它调用pickle.dump。当然,调用 pickle.dump 是不行的,因为每个项目都是在 unpickling 期间设置的。
有没有办法解决这个问题,除了蛮力(如下)?我尝试阅读Pickling Class Instances 以寻找使用特殊方法的解决方案,但没有找到。
下面的代码监控unpickling是否正在进行,在这种情况下跳过pickle.dump;虽然它工作正常,但感觉很hacky。
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()
【问题讨论】:
-
给你的类一个 dict 属性,并将数据存储在那里,而不是让你的类从 dict 继承可能更容易。然后你可以腌制存储的字典而不是你的 PersistentDict,将两层分开。
-
@BrenBarn 这正是我的想法,但我从一开始就非常反对继承,以至于我总是用组合替换它。所以这一次,我想尝试一下继承。我知道的唯一支持继承的论点是使用
__getattr__的自动转发不会转发特殊方法(如__getitem__、__contains__、__eq__等),而且转发有点麻烦他们都是手动的。但这似乎是另一个例子,继承比组合更令人沮丧。
标签: python python-3.x pickle