2010-08-04 4 views
8

Работа с глубоко вложенными питона dicts, я хотел бы иметь возможность присваивать значения в такой структуре данных, как это:Генерация ключей словаря на лету

mydict[key][subkey][subkey2]="value" 

без того, чтобы проверить, что mydict [ключ] и т.д. на самом деле заданы как dict, например используя

if not key in mydict: mydict[key]={} 

Создание подзаголовков должно происходить на лету. Какой самый элегантный способ позволить что-то эквивалентное - возможно, используя декораторы по стандарту <type 'dict'>?

+0

Связанный вопрос: http://stackoverflow.com/questions/3122566/in-a-python-dict-of-dicts-how-do-you-emulate-perls-auto-vivification-behavior/3122575#3122575 – unutbu

ответ

19
class D(dict): 
    def __missing__(self, key): 
     self[key] = D() 
     return self[key] 

d = D() 
d['a']['b']['c'] = 3 
+1

Вы должны быть осторожны с этим. 'd ['a'] = 2 d ['a'] ['b'] = 2' не удастся. – unholysampler

+0

Да, но это подразумевается в моем вопросе - я прошу смешанный тип диктов и ценностей. – relet

+0

Чтобы решить случай d ['a'] = 2 d ['a'] ['b'] = 2, я бы даже записал класс D (dict): def __missing __ (self, key): value = self [ключ] = тип (сам)() возвращаемого значения защиту __getitem __ (самостоятельно, ключ): значения = ДИКТ .__ GetItem __ (самостоятельно, ключ) , если isinstance (значения, Dict): значение = SafeDict (значения) если isinstance (значения, список): для I, V в перечислять (значения): если isinstance (v, ДИКТ): значения [я] = SafeDict (v) возврата значения – user2346922

9

Вы можете использовать кортеж в качестве ключа для Dict и тогда вам не придется беспокоиться о subdictionaries вообще:

mydict[(key,subkey,subkey2)] = "value" 

С другой стороны, если вам действительно нужно иметь subdictionaries по какой-то причине вы могли бы использование collections.defaultdict.

Для двух уровней это просто:

>>> from collections import defaultdict 
>>> d = defaultdict(dict) 
>>> d['key']['subkey'] = 'value' 
>>> d['key']['subkey'] 
'value' 

Для три это немного сложнее:

>>> d = defaultdict(lambda: defaultdict(dict)) 
>>> d['key']['subkey']['subkey2'] = 'value' 
>>> d['key']['subkey']['subkey2'] 
'value' 

Четыре и более уровней остается в качестве упражнения для читателя. :-)

+0

Удивительно. Я рад, что спросил, хотя бы потому, что не понимаю, как я мог это пропустить. :) – relet

+0

Хороший ответ. Можете ли вы указать, как это сделать с помощью 'defaultdict'? Вложение один раз легко: 'mydict = defaultdict (dict)'. Но есть ли элегантное решение для гнездования дважды? –

+0

@jellybean - просто добавлено решение на три уровня; это не так уж плохо. –

2

Мне нравится ответ Дэйва лучше, но вот альтернатива.

from collections import defaultdict 
d = defaultdict(lambda : defaultdict(int)) 
>>> d['a']['b'] += 1 
>>> d 
defaultdict(<function <lambda> at 0x652f0>, {'a': defaultdict(<type 'int'>, {'b': 1})}) 
>>> d['a']['b'] 
1 

http://tumble.philadams.net/post/85269428/python-nested-defaultdicts

Это, безусловно, не очень, чтобы использовать лямбды для реализует внутренние дефолтные коллекции, но, видимо, необходимо.

+2

Lambdas никогда не нужны: вы всегда можете использовать именованную функцию. В этом случае использование именованной функции, по крайней мере, означает, что 'repr' имеет нечто более значимое, чем' lambda'. – Duncan

Смежные вопросы