在 Python 中设置嵌套字典项时访问完整字典
Accessing full dictionary when setting a nested dictionary item in Python
我正在尝试创建一个字典子class,它允许创建一个字典 A,它将更新一个预先存在的字典 B 中的值,使其等于字典 A 的字符串表示。我明白了它作为一个观察者模式,没有观察多个对象的能力。
即:
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 现在是 {'observed': '{"foo": 1, "bar": {"foobar": 2}}'}
,A 是 {'foo': 1, 'bar': {'foobar': 2}}
。更新字典中的值有三种情况(暂时忽略update
和set
):
- 我可以更新 A 的顶级密钥,而且效果很好:
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}}'}
- 但是,如果我使用
[]
方法编辑嵌套值,__setitem__
中的 self
是嵌套字典,而不是包含 ObservedDict
class 被初始化,所以:
A['bar']['foobar'] = 4
# B is now {'observed': '{"foobar": 4}'}
我的问题是:如何保留有关父字典(即用于初始化 class 的字典)的信息,以便在使用第三种情况设置值时,字典 B
将更新并包含整个字典 A(在本例中为匹配案例 2)?
好吧,尽管我之前尝试过将父词典附加到嵌套词典,但没有成功,@MichaelButscher 的评论促使我再次尝试。下面是一个可行的解决方案,它似乎适用于使用 []
方法在嵌套字典中设置值,无论深度如何。
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
确保 'parent' 始终是第一次初始化对象时给出的 self
(即 A
)似乎可以解决问题。
为了使 class 更简单,您可以做的一件事是将更新 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)
然后你可以将它与 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)
或者用 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)
两者都给你预期的结果:
{'observed': '{"foo": 1, "bar": {"foobar": 2}}'}
{'observed': '{"foo": 2, "bar": {"foobar": 2}}'}
{'observed': '{"foo": 2, "bar": {"foobar": 4}}'}
{'observed': '{"foo": 2, "bar": {"foobar": 5}}'}
我正在尝试创建一个字典子class,它允许创建一个字典 A,它将更新一个预先存在的字典 B 中的值,使其等于字典 A 的字符串表示。我明白了它作为一个观察者模式,没有观察多个对象的能力。
即:
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 现在是 {'observed': '{"foo": 1, "bar": {"foobar": 2}}'}
,A 是 {'foo': 1, 'bar': {'foobar': 2}}
。更新字典中的值有三种情况(暂时忽略update
和set
):
- 我可以更新 A 的顶级密钥,而且效果很好:
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}}'}
- 但是,如果我使用
[]
方法编辑嵌套值,__setitem__
中的self
是嵌套字典,而不是包含ObservedDict
class 被初始化,所以:
A['bar']['foobar'] = 4
# B is now {'observed': '{"foobar": 4}'}
我的问题是:如何保留有关父字典(即用于初始化 class 的字典)的信息,以便在使用第三种情况设置值时,字典 B
将更新并包含整个字典 A(在本例中为匹配案例 2)?
好吧,尽管我之前尝试过将父词典附加到嵌套词典,但没有成功,@MichaelButscher 的评论促使我再次尝试。下面是一个可行的解决方案,它似乎适用于使用 []
方法在嵌套字典中设置值,无论深度如何。
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
确保 'parent' 始终是第一次初始化对象时给出的 self
(即 A
)似乎可以解决问题。
为了使 class 更简单,您可以做的一件事是将更新 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)
然后你可以将它与 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)
或者用 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)
两者都给你预期的结果:
{'observed': '{"foo": 1, "bar": {"foobar": 2}}'}
{'observed': '{"foo": 2, "bar": {"foobar": 2}}'}
{'observed': '{"foo": 2, "bar": {"foobar": 4}}'}
{'observed': '{"foo": 2, "bar": {"foobar": 5}}'}