长话短说
如何在为子键赋值时让 super 键在 Python 字典中自动生成,而不在检查子键时让它们自动生成?
背景: 通常在 Python 中,在嵌套字典中设置值需要手动确保更高级别的键在分配给它们的子键之前存在。也就是说,
my_dict[1][2] = 3
如果不先做类似的事情,将无法按预期可靠地工作
if 1 not in my_dict:
my_dict[1] = {}
现在,可以通过使 my_dict
成为覆盖 __missing__
的类的实例来设置一种自动生成,例如所示在 https://stackoverflow.com/a/19829714/6670909 .
问题:但是,如果您检查此类嵌套字典中是否存在子键,该解决方案会自动自动生成更高级别的键。这导致了以下不幸:
>>> vd = Vividict()
>>> 1 in vd
False
>>> 2 in vd[1]
False
>>> 1 in vd
True
我怎样才能避免这种误导性的结果?顺便说一句,在 Perl 中,我可以通过以下方式获得所需的行为
no autovivification qw/exists/;
基本上,如果可能的话,我想在 Python 中复制该行为。
最佳答案
这不是一个容易解决的问题,因为在您的示例中:
my_dict[1][2] = 3
my_dict[1]
导致对字典的 __getitem__
调用。在这一点上没有办法知道正在进行分配。只有序列中的最后一个 []
是一个 __setitem__
调用,除非 mydict[1]
存在,否则它不会成功,否则,你要分配给什么对象?
所以不要使用自动激活。您可以使用 setdefault()
代替,使用常规 dict
。
my_dict.setdefault(1, {})[2] = 3
现在这不是很漂亮,特别是当你嵌套得更深时,所以你可以编写一个辅助方法:
class MyDict(dict):
def nest(self, keys, value):
for key in keys[:-1]:
self = self.setdefault(key, {})
self[keys[-1]] = value
my_dict = MyDict()
my_dict.nest((1, 2), 3) # my_dict[1][2] = 3
但更好的方法是将它包装到一个新的 __setitem__
中,它一次获取所有索引,而不是需要中间的 __getitem__
调用来引发自动激活。这样,我们从一开始就知道我们正在执行一项任务,并且可以在不依赖自动激活的情况下继续进行。
class MyDict(dict):
def __setitem__(self, keys, value):
if not isinstance(keys, tuple):
return dict.__setitem__(self, keys, value)
for key in keys[:-1]:
self = self.setdefault(key, {})
dict.__setitem__(self, keys[-1], value)
my_dict = MyDict()
my_dict[1, 2] = 3
为了保持一致性,您还可以提供 __getitem__
接受元组中的键,如下所示:
def __getitem__(self, keys):
if not isinstance(keys, tuple):
return dict.__getitem__(self, keys)
for key in keys:
self = dict.__getitem__(self, key)
return self
我能想到的唯一缺点是我们不能轻松地将元组用作字典键:我们必须将其写为,例如my_dict[(1, 2),]
.
关于python - 仅在赋值时如何实现嵌套字典的自动生成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42122577/