我已经将Python MPI_ANY_TAG
子类化了,大概是这样的:
dict
我可以使用它,例如:
class JSONDict(dict):
@property
def __json__(self):
return json.dumps(self)
@property
def __path__(self):
return os.path.join(settings.BASE_PATH, self.entity, 'settings.json')
def __init__(self, entity, *args, **kwargs):
dict.__init__(self)
self.entity = entity
try:
os.utime(self.__path__, None)
except OSError:
open(self.__path__, 'a').close()
self.__file__ = open(self.__path__, 'r+')
self.__to_python__()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.__file__.close()
def __to_json__(self):
self.__file__.seek(0)
self.__file__.write(self.__json__)
self.__file__.flush()
self.__file__.truncate()
def __to_python__(self):
if os.path.getsize(self.__path__):
self.__file__.seek(0)
dict.clear(self)
dict.update(self, json.load(self.__file__))
else:
self.clear()
def __getitem__(self, key):
self.__to_python__()
return dict.__getitem__(self, key)
def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
self.__to_json__()
def update(self, data):
dict.update(self, data)
self.__to_json__()
def clear(self):
dict.clear(self)
self.update(__default__)
self['nar'] = self.nar
然而,当涉及到列表时,这是一个有趣的问题。当我直接修改列表时,例如>>> with JSONDict('entity-id') as json_info:
... json_info['setting_a'] = 'foo'
,JSON文件不会更新。我确信覆盖json_info['setting_b'].append('bar')
和__setitem__
会覆盖所有项目访问权限,但显然情况并非如此。我应该覆盖什么来完成这项工作?
更新:
在阅读了第一个答案之后,我觉得这个问题并没有以正确的方式制定。正确的问题是,如何有效地观察dict属性的变化,以便能够相应地更新JSON?它应该通过代理/描述符对象,元类来完成,还是有更通用的方式?
答案 0 :(得分:2)
正如Willem Van Onsem所提到的那样,变异list
并不会更新字典本身 - 你在JSONDict.__setitem__
做的时候并没有打电话给json_info['some_list'].append(whatever)
- 实际上你是这样做的对于任何可变对象都有同样的问题。
这里的技术解决方案是让__getitem__
返回包含实际对象和JSONDict
实例的代理,并覆盖__setattr__
以检测代理对象的任何更改,即:
class Proxy(object):
def __init__(self, jsdict, key, obj):
# avoids triggering `__setattr__`
self.__dict__["__jsdict"] = jsdict
self.__dict__["__key"] = key
self.__dict__["__obj"] = obj
def __setattr__(self, name, value):
setattr(self.__obj, name, value)
self.__jsdict[self.__key] = self.__obj
def __getattr__(self, name):
return getattr(self.__obj, name)
class JSONDict(dict):
# ...
def __getitem__(self, key):
self.__to_python__()
return Proxy(self, key, dict.__getitem__(self, key))
现在这仍然不适用于列表(或其他容器),因此您还需要实现Proxy.__setitem__
。由于JSONDict
中包含的列表可以包含可变对象,因此您需要确保您还代理JSONDict中包含的任何容器...
哦,是的,在代理对象上查找的任何属性也应该被代理,以便json_info['some_list'][42].foo.baaz = 'wtf'
按预期工作。
好的我认为你现在明白了这一点:以一种通用,可靠的方式进行这项工作将很困难。更不用说表演了......
您可能拥有更可靠的解决方案,只需保存到__exit__
上的文件和/或为最终用户提供明确的save()
方法。这也可以节省(没有双关语意)磁盘写入......