Установите вложенное значение dict и создайте промежуточные ключи

Мне кажется, что я видел способ сделать это недавно. Скажем, у меня пустой dict, и я хочу установить значение в вложенном dict внутри этого пустого dict, но очевидно, что вложенный dict еще не создан. Есть ли 1-строчный способ создания промежуточных ключей? Это то, что я хочу сделать:

mydict = {}
mydict['foo']['bar']['foobar'] = 25

Если вы выполните этот код, вы получите исключение KeyError для 'foo'. Есть ли функция для создания промежуточных ключей?

Спасибо.

Ответ 1

from collections import defaultdict
recursivedict = lambda: defaultdict(recursivedict)
mydict = recursivedict()

При доступе к mydict['foo'] он устанавливает mydict['foo'] в другой recursivedict. Он фактически построит recursivedict для mydict['foo']['bar']['foobar'], но затем он будет выброшен, назначив это 25.

Ответ 2

Альтернативный вариант - в зависимости от ваших применений - использовать кортежи как ключи вместо вложенных словарей:

mydict = {}
mydict['foo', 'bar', 'foobar'] = 25

Это будет отлично работать, если вы не захотите получить ветку дерева в любой точке (вы не можете получить mydict ['foo'] в этом случае).

Если вы знали, сколько слоев вложенности вы хотите, вы также можете использовать functools.partial вместо лямбда.

from functools import partial
from collections import defaultdict

tripledict = partial(defaultdict, partial(defaultdict, dict))
mydict = tripledict()
mydict['foo']['bar']['foobar'] = 25

Что некоторые люди находят более читаемыми и быстрее создают экземпляры, чем эквивалентное лямбда-решение:

python -m timeit -s "from functools import partial" -s "from collections import defaultdict" -s "tripledefaultdict = partial(defaultdict, partial(defaultdict, dict))" "tripledefaultdict()"
1000000 loops, best of 3: 0.281 usec per loop

python -m timeit -s "from collections import defaultdict" -s "recursivedict = lambda: defaultdict(recursivedict)" "recursivedict()"
1000000 loops, best of 3: 0.446 usec per loop

Хотя, как всегда, оптимизация точки не существует, пока вы не знаете, есть ли узкое место, поэтому выберите наиболее полезную и читаемую информацию до того, что будет быстрее.

Ответ 3

Не уверен, зачем вам это нужно, но:

>>> from collections import defaultdict as dd
>>> mydict = dd(lambda: dd(lambda: {}))
>>> mydict['foo']['bar']['foobar'] = 25
>>> mydict
defaultdict(<function <lambda> at 0x021B8978>, {'foo': defaultdict(<function <lambda> at 0x021B8618>, {'bar': {'foobar': 25}})})