简体   繁体   English

python collections.defaultdict分配但不查找

[英]python collections.defaultdict on assignment but not on lookup

I have the following code: 我有以下代码:

from collections import *
nested_dict = lambda: defaultdict(nested_dict)
data = nested_dict()

which enables me to write any new "path" in the dict as a one liner: 这使我能够将字典中的任何新“路径”写成一行:

data['A']['B']['C']=3

which is what I want. 这就是我想要的。 But I want to get an exception when running (for any non existing path): 但是我想在运行时得到一个异常(对于任何不存在的路径):

var = data['A']['XXX']['C']

I feel I need defaultdict when writing, plain dict when reading... 我觉得我在写作时需要defaultdict,在阅读时需要朴素的字典...

Or, is there a simple nice way to check if a 'path' exists in a defaultdict without modifying its contents... 或者,是否有一种简单的好方法来检查defaultdict中是否存在``路径''而不修改其内容...

I tried converting the defaultdict back to a dict before the lookup, hoping that: 我尝试在查询之前将defaultdict转换回dict,希望:

dict(data)['A']['XXX']['C']

would raise a exception... but it kept creating missing keys... 会引发异常...但是它一直在创建丢失的键...

An obvious solution is to just use plain dicts with a function that can "materialize" the intermediate keys: 一个明显的解决方案是仅将简单的dict与可以“具体化”中间键的函数一起使用:

def write_path(d, path, value):
    for key in path[:-1]:
        d = d.setdefault(key, {})
    d[path[-1]] = value

d = {}

write_path(d, ['a', 'b', 'c'], 3)
print(d)
print(d['a']['b']['c'])
print(d['a']['b']['d'])

outputs 输出

{'a': {'b': {'c': 3}}}
3
Traceback (most recent call last):
  File "writedefaultdict.py", line 11, in <module>
    print(d['a']['b']['d'])
KeyError: 'd'

You can't distingsuish between lookups and writes here, because it is the lookups that create your intermediary structure in the data['A']['B']['C'] = 3 assignment. 您无法在查找和写入之间区分开来,因为查找是在data['A']['B']['C'] = 3分配中创建中介结构的。 Python executes the indexing operations data['A'] and then ['B'] first, before assigning to the 'C' key. 在分配给'C'键之前,Python首先执行索引操作data['A'] ,然后首先执行['B'] The __getitem__ , __setitem__ and __missing__ hooks involved to make that work are not given enough context to distinguish between access that then leads to the 'C' assignment from only 'reading' 'XXX' in your second example. __getitem____setitem____missing__参与,使这项工作挂钩没有给予足够的情况下访问之间进行区分,然后导致了'C'分配只从“阅读” 'XXX'在你的第二个例子。

You really only have 3 options here: 您实际上只有3个选项:

  • Don't use defaultdict . 不要使用defaultdict When writing, explicitly create new nested dictionaries with dict.setdefault() instead; 编写时,请使用dict.setdefault() 显式创建新的嵌套字典; you can chain these calls as needed: 您可以根据需要链接这些调用:

     var = {} var.setdefault('A', {}).setdefault('B', {})['C'] = 3 

    or you can wrap recursive behaviour in a few functions . 或者您可以将递归行为包装在几个函数中

  • Create a recursive copy of your defaultdict structure to replace it with a dict structure once you are done writing: 创建defaultdict结构的递归副本,以在完成编写后将其替换为dict结构:

     def dd_to_d(dd): r = {} stack = [(r, dd)] while stack: target, dd = stack.pop() for key, value in dd.items(): if isinstance(value, defaultdict): sub = {} stack.append((sub, value)) value = sub target[key] = value return r var = dd_to_d(var) 
  • Set all the default_factory attributes to None to disable creating new values for missing keys: 将所有default_factory属性设置为None以禁用为丢失的键创建新值:

     def disable_dd(dd): stack = [dd] while stack: dd = stack.pop() dd.default_factory = None for key, value in dd.items(): if isinstance(value, defaultdict): stack.append(value) disable_dd(var) 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM