Вот рекурсивное решение, которое может обрабатывать произвольно вложенные словари:
>>> import collections
>>> def dict_find_recursive(d, target):
... if not isinstance(d, collections.Mapping):
... return d == target
... else:
... for k in d:
... if dict_find_recursive(d[k], target) != False:
... return k
... return False
В долгосрочной перспективе это не так эффективно, как «обратный словарь», но если вы не выполняете такие обратные поиски часто,это, вероятно, не имеет значения.(Обратите внимание, что вы должны явно сравнить результат dict_find_recursive(d[k], target)
с False
, потому что в противном случае ложные ключи, такие как ''
, приводят к сбою поиска. На самом деле, даже эта версия не срабатывает, если в качестве ключа используется False
;Полностью общее решение будет использовать уникальный часовой object()
для обозначения фальши.)
Несколько примеров использования:
>>> d = {'dict1': {3: 'three', 4: 'four'}, 'dict2': {1: 'one', 2: 'two'}}
>>> dict_find_recursive(d, 'two')
'dict2'
>>> dict_find_recursive(d, 'five')
False
>>> d = {'dict1': {3: 'three', 4: 'four'}, 'dict2': {1: 'one', 2: 'two'},
'dict3': {1: {1:'five'}, 2: 'six'}}
>>> dict_find_recursive(d, 'five')
'dict3'
>>> dict_find_recursive(d, 'six')
'dict3'
Если вы хотите отменить произвольно вложенный набор словарей, рекурсивные генераторыВаш друг:
>>> def dict_flatten(d):
... if not isinstance(d, collections.Mapping):
... yield d
... else:
... for value in d:
... for item in dict_flatten(d[value]):
... yield item
...
>>> list(dict_flatten(d))
['three', 'four', 'five', 'six', 'one', 'two']
Выше просто перечисляются все значения в словаре, которые не являются отображениями.Затем вы можете сопоставить каждое из этих значений с ключом, например, так:
>>> def reverse_nested_dict(d):
... for k in d:
... if not isinstance(d[k], collections.Mapping):
... yield (d[k], k)
... else:
... for item in dict_flatten(d[k]):
... yield (item, k)
...
Это генерирует итеративный набор кортежей, поэтому информация не теряется:
>>> for tup in reverse_nested_dict(d):
... print tup
...
('three', 'dict1')
('four', 'dict1')
('five', 'dict3')
('six', 'dict3')
('one', 'dict2')
('two', 'dict2')
Если вы знаете, что всеваши не отображаемые значения могут быть хэшируемыми - и если вы знаете, что они уникальны, или если вам не нужны коллизии - просто передайте получившиеся кортежи в dict()
:
>>> dict(reverse_nested_dict(d))
{'six': 'dict3', 'three': 'dict1', 'two': 'dict2', 'four': 'dict1',
'five': 'dict3', 'one': 'dict2'}