2013-04-14 2 views
11

У меня есть многомерный dict, и я бы хотел получить значение с помощью пары key: key и вернуть «NA», если первый ключ не существует , Все поддиты имеют одинаковые ключи.Python dict.get() с многомерным dict

d = { 'a': {'j':1,'k':2}, 
     'b': {'j':2,'k':3}, 
     'd': {'j':1,'k':3} 
    } 

Я знаю, что могу использовать d.get('c','NA') получить суб-Dict, если она существует, и возвращение «NA» в противном случае, но мне действительно нужно только одно значение из суб-Dict. Я бы хотел сделать что-то вроде d.get('c['j']','NA'), если это существовало.

Прямо сейчас я просто проверяю, существует ли ключ верхнего уровня, а затем присваивает субъекту значение переменной, если оно существует, или «NA», если нет. Тем не менее, я делаю это примерно в 500 тыс. Раз, а также извлекаю/генерирую другую информацию о каждом ключе верхнего уровня из других источников, и я пытаюсь немного ускорить это.

ответ

20

Как насчет

d.get('a', {'j': 'NA'})['j'] 

?

Если не все subdicts имеют j ключ, затем

d.get('a', {}).get('j', 'NA') 

 

Чтобы сократить идентичных объектов, созданных, вы можете придумать что-то вроде

class DefaultNASubdict(dict): 
    class NADict(object): 
     def __getitem__(self, k): 
      return 'NA' 

    NA = NADict() 

    def __missing__(self, k): 
     return self.NA 

nadict = DefaultNASubdict({ 
       'a': {'j':1,'k':2}, 
       'b': {'j':2,'k':3}, 
       'd': {'j':1,'k':3} 
      }) 

print nadict['a']['j'] # 1 
print nadict['b']['j'] # 2 
print nadict['c']['j'] # NA 

 

Такая же идея используя defaultdict:

import collections 

class NADict(object): 
    def __getitem__(self, k): 
     return 'NA' 

    @staticmethod 
    def instance(): 
     return NADict._instance 

NADict._instance = NADict() 


nadict = collections.defaultdict(NADict.instance, { 
       'a': {'j':1,'k':2}, 
       'b': {'j':2,'k':3}, 
       'd': {'j':1,'k':3} 
      }) 
+0

посмотреть на 'collections.defaultdict' для уже при условии реализации, то есть' defaultdict (лямбда: defaultdict (лямбда: 'NA')) ' – mtadd

+0

Уверен, но вам все равно нужен' NADict' и функция, которая возвращает общий экземпляр. Я добавлю пример. –

+0

@mtadd: идея заключалась в том, чтобы не создавать новый dict/defaultdict для каждого mislookup. –

2

Вместо иерархии вложенных dict объектов, вы можете использовать один словарь, ключи которого кортеж, представляющий собой путь по иерархии.

In [34]: d2 = {(x,y):d[x][y] for x in d for y in d[x]} 

In [35]: d2 
Out[35]: 
{('a', 'j'): 1, 
('a', 'k'): 2, 
('b', 'j'): 2, 
('b', 'k'): 3, 
('d', 'j'): 1, 
('d', 'k'): 3} 

In [36]: timeit [d[x][y] for x,y in d2.keys()] 
100000 loops, best of 3: 2.37 us per loop 

In [37]: timeit [d2[x] for x in d2.keys()] 
100000 loops, best of 3: 2.03 us per loop 

Доступ к этому способу выглядит примерно на 15% быстрее. Вы все еще можете использовать метод get со значением по умолчанию:

In [38]: d2.get(('c','j'),'NA') 
Out[38]: 'NA' 
4

Вот простой и эффективный способ сделать это с помощью обычных словарей, вложенной произвольного числа уровней:

d = {'a': {'j': 1, 'k': 2}, 
    'b': {'j': 2, 'k': 3}, 
    'd': {'j': 1, 'k': 3}, 
    } 

def chained_get(dct, *keys): 
    SENTRY = object() 
    def getter(level, key): 
     return 'NA' if level is SENTRY else level.get(key, SENTRY) 
    return reduce(getter, keys, dct) 

print chained_get(d, 'a', 'j') # 1 
print chained_get(d, 'b', 'k') # 3 
print chained_get(d, 'k', 'j') # NA 

Это также может быть сделано рекурсивно:

def chained_get(dct, *keys): 
    SENTRY = object() 
    def getter(level, keys): 
     return (level if keys[0] is SENTRY else 
        'NA' if level is SENTRY else 
         getter(level.get(keys[0], SENTRY), keys[1:])) 
    return getter(dct, keys+(SENTRY,)) 

Хотя этот способ сделать это не так эффективен, как первый.

0

Другой способ получить многомерный пример Dict (используйте метод GET дважды)

d.get('a', {}).get('j') 
Смежные вопросы