在Python中同步属性更改

  • 本文关键字:属性 同步 Python python
  • 更新时间 :
  • 英文 :


我已经将Pythondict子类化,大致如下:

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_info['setting_b'].append('bar'),JSON文件不会得到更新。我确信覆盖__setitem____getitem__将覆盖所有项目访问,但显然不是这样。我应该重写什么才能完成此工作?

更新:

看完第一个答案后,我觉得这个问题的表述方式不对。正确的问题是,如何有效地观察dict属性的变化,以便能够相应地更新JSON?它应该通过代理/描述符对象、元类来完成,还是有更通用的方法?

正如Willem Van Onsem所提到的,更改list不会更新dict本身-在执行json_info['some_list'].append(whatever)时不会调用JSONDict.__setitem__-实际上,对于任何可变对象都会遇到同样的问题。

这里的技术解决方案是让__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()方法。这也可以节省(并非双关语)磁盘写入。。。

最新更新