[英]Accessing full dictionary when setting a nested dictionary item in Python
I'm trying to create a dictionary subclass which allows a dictionary A to be created which will update a value in a pre-exiting dictionary B to equal a string representation of dictionary A. I see it as an observer pattern, without the ability to have multiple objects observing.我正在尝试创建一个字典子类,它允许创建一个字典 A,它将更新预先存在的字典 B 中的值以等于字典 A 的字符串表示形式。我将其视为观察者模式,没有能力有多个对象观察。
ie: IE:
import json
from collections import Mapping
class ObservedDict(dict):
def __init__(self, initial_dict, name=None, observer=None, top_level=True):
for k, v in initial_dict.items():
if isinstance(v, dict):
initial_dict[k] = ObservedDict(v, name, observer, top_level=False)
super().__init__(initial_dict)
self.name = name
self.observer = observer
if top_level is True: # initialise the key:value pair in B
observer[name] = json.dumps(initial_dict)
def __setitem__(self, item, value):
if isinstance(value, dict):
_value = ObservedDict(value, self.name, self.observer, top_level=False)
else:
_value = value
super().__setitem__(item, _value)
# Update B
self.observer[self.name] = json.dumps(self)
B = {}
A = ObservedDict({'foo': 1, 'bar': {'foobar': 2}}, 'observed', B)
B is now {'observed': '{"foo": 1, "bar": {"foobar": 2}}'}
and A is {'foo': 1, 'bar': {'foobar': 2}}
. B 现在是{'observed': '{"foo": 1, "bar": {"foobar": 2}}'}
而 A 是{'foo': 1, 'bar': {'foobar': 2}}
。 There are three cases for updating a value in the dictionary (ignoring update
and set
for now):更新字典中的值有三种情况(暂时忽略update
和set
):
A['foo'] = 2
# B is now automatically {'observed': '{"foo": 2, "bar": {"foobar": 2}}'}
A['bar'] = {'foobar': 4}
# B is now automatically {'observed': '{"foo": 2, "bar": {"foobar": 4}}'}
[]
method, self
in __setitem__
is the nested dict, not the whole dictionary with which the ObservedDict
class is initialised, so:但是,如果我使用[]
方法编辑嵌套值,则__setitem__
中的self
是嵌套字典,而不是初始化ObservedDict
类的整个字典,因此:A['bar']['foobar'] = 4
# B is now {'observed': '{"foobar": 4}'}
My question is: how do I retain information about the parent dictionary (ie the one used to initialise the class) such that on setting a value using the third case, dictionary B
will update and include the whole of dictionary A (matching case 2, in this instance)?我的问题是:如何保留有关父字典(即用于初始化类的字典)的信息,以便在使用第三种情况设置值时,字典B
将更新并包含整个字典 A(匹配情况 2,在这种情况下)?
OK, so although I had played around with attaching the parent dictionary to the nested dictionaries before, without luck, @MichaelButscher's comment spurred me to try again.好的,所以尽管我之前尝试将父字典附加到嵌套字典中,但运气不佳,@MichaelButscher 的评论促使我再次尝试。 Below is a working solution, which seems to work for setting a value in a nested dictionary using the []
method, no matter the depth.下面是一个有效的解决方案,它似乎适用于使用[]
方法在嵌套字典中设置值,无论深度如何。
import json
from collections import Mapping
class ObservedDict(dict):
def __init__(self, initial_dict, name=None, observer=None, parent=None):
for k, v in initial_dict.items():
if isinstance(v, dict):
_parent = self if parent is None else parent
initial_dict[k] = ObservedDict(v, name, observer, parent=_parent)
super().__init__(initial_dict)
self.observer = observer
self.name = name
self.parent = parent
if parent is None: # initialise the key:value pair in B
observer[name] = json.dumps(initial_dict)
def __setitem__(self, item, value):
if isinstance(value, dict):
_value = ObservedDict(value, self.name, self.observer, parent=self.parent)
else:
_value = value
super().__setitem__(item, _value)
# Update B
if self.parent is not None:
self.observer[self.name] = json.dumps(self.parent) # nested dict
else:
self.observer[self.name] = json.dumps(self) # the top-level dict
Ensuring the 'parent' was always the self
as given when initialising the object for the first time (ie A
) seems to do the trick.在第一次初始化对象(即A
)时,确保“父母”始终是给定的self
似乎可以解决问题。
One thing you can do to make the class simpler is externalize the behavior of updating B
, like so:为了使类更简单,您可以做的一件事是将更新B
的行为外部化,如下所示:
class ObservedDict(dict):
def __init__(self, initial_dict, on_changed=None):
super().__init__(initial_dict)
self.on_changed = on_changed
for k, v in initial_dict.items():
if isinstance(v, dict):
super().__setitem__(
k, ObservedDict(v, on_changed=self.notify))
self.notify()
def __setitem__(self, key, value):
if isinstance(value, dict):
value = ObservedDict(value, on_changed=self.notify)
super().__setitem__(key, value)
self.notify()
def notify(self, updated=None):
if self.on_changed is not None:
self.on_changed(self)
Then you can use it with a lambda:然后你可以将它与 lambda 一起使用:
import json
B = {}
A = ObservedDict(
{'foo': 1, 'bar': {'foobar': 2}},
lambda d: B.update({'observed': json.dumps(d)}))
print(B)
A['foo'] = 2
print(B)
A['bar'] = {'foobar': 4}
print(B)
A['bar']['foobar'] = 5
print(B)
Or with a child class或与儿童班
class UpdateObserverDict(ObservedDict):
def __init__(self, *args, name, observer, **kwargs):
self.observer = observer
self.name = name
super().__init__(*args, **kwargs)
def notify(self, updated=None):
self.observer[self.name] = json.dumps(self)
B = {}
A = UpdateObserverDict(
{'foo': 1, 'bar': {'foobar': 2}},
name='observed', observer=B)
print(B)
A['foo'] = 2
print(B)
A['bar'] = {'foobar': 4}
print(B)
A['bar']['foobar'] = 5
print(B)
both of which give you the expected result:两者都给你预期的结果:
{'observed': '{"foo": 1, "bar": {"foobar": 2}}'}
{'observed': '{"foo": 2, "bar": {"foobar": 2}}'}
{'observed': '{"foo": 2, "bar": {"foobar": 4}}'}
{'observed': '{"foo": 2, "bar": {"foobar": 5}}'}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.