Reputation: 1191
I keep a dictionary that's being modified through a user-interface. Instead of having a user-controlled 'save' functionality, I'd like to have the dictionary written into a JSON file whenever it changes. I know the mechanics of saving, but not quite how to trigger the save on a change in the dictionary. Is this possible, and if so, how?
Upvotes: 2
Views: 103
Reputation: 1121884
You cannot do it with the default dict
type, but you can use a custom dictionary subclass or a subclass of the collections.MutableMapping
abstract base class and intercept methods that alter the dictionary so you can trigger a save.
It's easiest with the latter; it maps all modifying methods to __setitem__
. You need to provide all methods in the Abstract Methods column of the overview table.
Here is my quick sketch of such a class:
import json
from collections import MutableMapping
class JSONBackedMapping(MutableMapping):
def __init__(self, filename, initial=None, **kw):
self._filename = filename
try:
# Try and load the file
self.load()
except (ValueError, IOError):
# failure, fall back to the initial object
self._data = initial or {}
self._data.update(**kw)
def load(self):
with open(self._filename, 'r') as inf:
self._data = json.load(inf)
def save(self):
with open(self._filename, 'w') as outf:
json.dump(self._data, outf)
def __repr__(self):
return '<{}({!r}, {})>'.format(
type(self).__name__,
self._filename, self._data)
def __len__(self): return len(self._data)
def __iter__(self): return iter(self._data)
def __getitem__(self, item): return self._data[item]
def __delitem__(self, item):
del self._data[item]
self.save()
def __setitem__(self, item, value):
self._data[item] = value
self.save()
This will load from the given filename, or if that fails start with an initial dictionary. You can add additional key-value pairs as keyword arguments. Any change made to it will automatically be saved as JSON to the given filename:
>>> data = JSONBackedMapping('data.json')
>>> data
<JSONBackedMapping('data.json', {})>
>>> data['foo'] = 'bar'
>>> data['spam'] = ['eggs', 'ham']
>>> print open('data.json').read()
{"foo": "bar", "spam": ["eggs", "ham"]}
>>> del data
>>> data = JSONBackedMapping('data.json')
>>> data
<JSONBackedMapping('data.json', {u'foo': u'bar', u'spam': [u'eggs', u'ham']})>
Upvotes: 4