Инвертирование словаря с наборами / списками в качестве значений

#python #python-3.x #dictionary

#python #python-3.x #словарь

Вопрос:

Я написал функцию для инвертирования словаря (проверено по многим другим сообщениям StackExchange о подобных задачах), ключами которого являются строки, а значениями — наборы (строк):

 def invert_dict(d: Dict[str, set]) -> Dict[str, set]:
    """
    Returns inverted dictionary (with original dictionary, d, whose keys are
    strings and values are sets containing strings of which occur in the
    keys of d).

    Useful for indexing graph in opposite direction (i.e., values inbound to
    key vs. values outbound to key).
    """
    inverse_dict = dict.fromkeys(d, set())
    for key in d.keys():
        for item in d[key]:
            inverse_dict[item].add(key)

    return inverse_dict

  

Что я нахожу совершенно ошеломляющим, так это то, что добавление строки к одному значению / набору (связанному с одним конкретным ключом) фактически добавляет указанную строку ко всем значениям / наборам в результирующем словаре!

Согласно приведенному выше коду, вызывающая беспокойство строка inverse_dict[item].add(key) .

Например, рассмотрим следующий словарь:

 original_dict = {'4.html': {'2.html'}, 
                 '3.html': {'4.html', '2.html'}, 
                 '2.html': {'3.html', '1.html'}, 
                 '1.html': {'2.html'}}
  

Когда я добавляю print операторы до и после inverse_dict[item].add(key) и запускаю invert_dict(original_dict) , я получаю следующее, напечатанное на консоли:

 Adding 4.html to the set associated with 2.html
{'4.html': {'4.html'}, '3.html': {'4.html'}, '2.html': {'4.html'}, '1.html': {'4.html'}}

Adding 3.html to the set associated with 4.html
{'4.html': {'3.html', '4.html'}, '3.html': {'3.html', '4.html'}, '2.html': {'3.html', '4.html'}, '1.html': {'3.html', '4.html'}}

Adding 3.html to the set associated with 2.html
{'4.html': {'3.html', '4.html'}, '3.html': {'3.html', '4.html'}, '2.html': {'3.html', '4.html'}, '1.html': {'3.html', '4.html'}}

Adding 2.html to the set associated with 3.html
{'4.html': {'3.html', '4.html', '2.html'}, '3.html': {'3.html', '4.html', '2.html'}, '2.html': {'3.html', '4.html', '2.html'}, '1.html': {'3.html', '4.html', '2.html'}}

Adding 2.html to the set associated with 1.html
{'4.html': {'3.html', '4.html', '2.html'}, '3.html': {'3.html', '4.html', '2.html'}, '2.html': {'3.html', '4.html', '2.html'}, '1.html': {'3.html', '4.html', '2.html'}}

Adding 1.html to the set associated with 2.html
{'4.html': {'1.html', '3.html', '4.html', '2.html'}, '3.html': {'1.html', '3.html', '4.html', '2.html'}, '2.html': {'1.html', '3.html', '4.html', '2.html'}, '1.html': {'1.html', '3.html', '4.html', '2.html'}}

  

Что дает? Я пробовал d.keys() заменять d.items() и использовать списки вместо наборов, но результаты были такими же.

Я использую Python 3.7.6.

Ответ №1:

dict.fromkeys(d, set()) просто создает один set и помещает его в каждую словарную запись. Вам нужно создать новый набор для каждой записи:

 inverse_dict = {k: set() for k in d}
  

Комментарии:

1. Спасибо, @Barmar! Просто читал это, как вы ответили (из документов Python): «Все значения относятся только к одному экземпляру, поэтому обычно не имеет смысла, чтобы значение было изменяемым объектом, таким как пустой список. Чтобы получить различные значения, вместо этого используйте понимание dict. » Как неловко. :/