在Python中同步属性更改

时间:2017-02-14 11:20:44

标签: python

我已经将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?它应该通过代理/描述符对象,元类来完成,还是有更通用的方式?

1 个答案:

答案 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()方法。这也可以节省(没有双关语意)磁盘写入......