Как получить из python dict, где ключ только частично известен?

У меня есть dict, который имеет строковые ключи, чьи точные значения я не могу знать (потому что они генерируются динамически в другом месте). Тем не менее, я знаю, что ключ, который я хочу, содержит определенную подстроку и что один ключ с этой подстрокой определенно находится в dict.

Какой лучший или "самый пифонический" способ получить значение для этого ключа?

Я думал о двух стратегиях, но оба раздражали меня:

for k,v in some_dict.items():
    if 'substring' in k:
        value = v
        break

- ИЛИ -

value = [v for (k,v) in some_dict.items() if 'substring' in k][0]

Первый метод является громоздким и несколько уродливым, а второй - более чистым, но лишний шаг индексирования в понимание списка ([0]) раздражает меня. Есть ли лучший способ выразить вторую версию или более сжатый способ написать первый?

Ответ 1

Существует возможность записать вторую версию с атрибутами производительности первого.

Используйте выражение вместо понимания списка:

value = next(v for (k,v) in some_dict.iteritems() if 'substring' in k)

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

Ответ 2

Как насчет этого:

value = (v for (k,v) in some_dict.iteritems() if 'substring' in k).next()

Он немедленно остановится, когда найдет первое совпадение.

Но он все еще имеет сложность O (n), где n - количество пар ключ-значение. Вам нужно что-то вроде списка суффиксов или дерева суффиксов, чтобы ускорить поиск.

Ответ 3

class MyDict(dict):
    def __init__(self, *kwargs):
        dict.__init__(self, *kwargs)

    def __getitem__(self,x):
        return next(v for (k,v) in self.iteritems() if x in k)



# Defining several dicos ----------------------------------------------------    
some_dict = {'abc4589':4578,'abc7812':798,'kjuy45763':1002}

another_dict = {'boumboum14':'WSZE x478',
                'tagada4783':'ocean11',
                'maracuna102455':None}

still_another = {12:'jfg',45:'klsjgf'}



# Selecting the dicos whose __getitem__ method will be changed -------------       
name,obj = None,None
selected_dicos = [ (name,obj) for (name,obj) in globals().iteritems()
                   if type(obj)==dict
                   and all(type(x)==str for x in obj.iterkeys())]

print 'names of selected_dicos ==',[ name for (name,obj) in selected_dicos] 



# Transforming the selected dicos in instances of class MyDict -----------
for k,v in selected_dicos:
    globals()[k] = MyDict(v)



# Exemple of getting a value ---------------------------------------------      
print "some_dict['7812'] ==",some_dict['7812']

результат

names of selected_dicos == ['another_dict', 'some_dict']
some_dict['7812'] == 798

Ответ 4

Если имеется много ключей, но строка легко восстанавливается из подстроки, то она может быть быстрее восстановлена. например часто вы знаете начало ключа, но не дату, которая была добавлена. (поэтому вам может потребоваться только 365 дат, а не повторять миллионы ключей, например). Это вряд ли будет так, но я думал, что все равно предлагаю. например.

>>> names={'bob_k':32,'james_r':443,'sarah_p':12}
>>> firstname='james' #you know the substring james because you have a list of firstnames
>>> for c in "abcdefghijklmnopqrstuvwxyz":
...     name="%s_%s"%(firstname,c)
...     if name in names:
...             print name
... 
james_r

Ответ 5

Я предпочитаю первую версию, хотя я бы использовал some_dict.iteritems() (если вы на Python 2), потому что вам не нужно заранее создавать полный список всех элементов. Вместо этого вы итерации через dict и перерыв, как только вы закончите.

В Python 3, some_dict.items(2) уже приводит к представлению словаря, так что уже подходящий итератор.