Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs
На этом занятии
мы с вами познакомимся с классом ChainMap модуля collections, который
позволяет управлять последовательностью из нескольких словарей (в общем случае,
картами, которые оперируют ключами и соответствующими значениями). О чем здесь
речь? Давайте предположим, что имеются два следующих словаря:
d1 = {'a': 1, 'b': 2}
d2 = {'b': 3, 'c': 4}
У них есть
одинаковый ключ 'b' с разными значениями. Данные этих словарей можно
объединить с помощью ChainMap следующим образом:
В итоге ch будет
представлять собой объект ChainMap с набором из двух словарей:
ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4})
Объект ch поддерживает те
же операции и методы, что и обычный словарь. Например:
ch['a'] # 1 (значение из 1-го словаря)
ch['b'] # 2 (значение общего ключа из 1-го словаря)
ch['c'] # 4 (значение из 2-го словаря)
ch['d'] # KeyError
Обратите
внимание, что ChainMap просматривает словари в порядке их добавления. И как
только встречается указанный ключ, то возвращается соответствующее значение.
Поэтому для ключа 'b' объект ch вернул значение
2, а не 3. А вот если бы словари были указаны в другом порядке:
то по ключу 'b'
будет извлечено значение 3:
Также следует
обратить внимание, что объект класса ChainMap не создает нового объединенного
словаря, а работает непосредственно с первоисточником. В нашем примере – это
словари d1, d2. Такой подход
позволяет заметно экономить память устройства, лишь имитируя объединение
словарей. Например, если изменить данные в каком-либо из словарей:
то команда:
вернет это
измененное значение 5.
Надо сказать,
что класс ChainMap разработан, главным образом, для считывания данных из набора
карт (словарей). Конечно, никто не запрещает присваивать новые значения через объекты
этого класса, например, так:
Это приведет к
ожидаемому изменению в словаре d1:
{'a': 'hello', 'b': 2}
Однако если
указать не существующий в d1 ключ:
то эта новая
пара будет добавлена в словарь d1, а не в словарь d2, где этот ключ
уже существует:
d1: {'a': 'hello', 'b': 2, 'c': -1}
d2: {'b': 3, 'c': 4}
Поэтому операции
присваивания через объекты класса ChainMap лучше избегать.
Метод new_child()
Далее, если к
уже существующему набору словарей d1, d2 нужно добавить
еще какие-либо данные, то это можно сделать с помощью метода new_child()
следующим образом:
ch_new = ch.new_child({'a': 100, 'd': 7, 'key': [1, 2, 3]})
Этот метод
возвращает новый объект типа ChainMap, с набором данных:
ChainMap({'a':
100, 'd': 7, 'key': [1, 2, 3]}, {'a': 1, 'b': 2}, {'b': 3, 'c': 4})
То есть,
словарь, прописанный в аргументе метода new_child() ставится на
первую позицию, и все его данные просматриваются объектом ch_new в первую
очередь. Поэтому для ключа 'a' получим значение 100:
И дополнительно
будем иметь ключи 'd' и 'key':
ch_new['key'] # [1, 2, 3]
Этот метод
бывает весьма полезен, когда существующий набор нужно обновить дополнительными
данными. Например, когда глобальная настройка приложения дополняется локальной с
переопределением некоторых ключей.
Переупорядочивание списка карт (словарей)
Как отмечалось
вначале, при создании объектов ChainMap формируется строгий порядок следования
словарей (в общем случае карт):
d1 = {'a': 1, 'b': 2}
d2 = {'b': 3, 'c': 4}
ch = ChainMap(d1, d2) # ChainMap({'a': 1, 'b': 2}, {'b': 3, 'c': 4})
Этот порядок
сохраняется в списке, доступный через атрибут maps:
ch.maps # [{'a': 1, 'b': 2}, {'b': 3, 'c': 4}]
Так как список –
это изменяемый объект, то порядок следования можно легко поменять, например, на
противоположный:
ch.maps.reverse() # [{'b': 3, 'c': 4}, {'a': 1, 'b': 2}]
Тогда по общему
ключу 'b' будет возвращаться значение 3, а не 2:
При этом связь с
исходными словарями d1, d2 сохраняется, и при изменении:
увидим изменения
и в списке maps:
ch.maps # [{'b': -1, 'c': 4}, {'a': 1, 'b': 2}]
Области применения
Класс ChainMap
имеет смысл применять для следующих задач:
- эффективный
поиск: быстрое получение первого подходящего значения ключа;
- облегченный способ
управления несколькими источниками данных, особенно для конфигураций, где
глобальная настройка дополняется локальной с переопределением некоторых ключей;
- поддержка
динамического добавления новых слоев (new_child), что упрощает работу с
настройками и обновлениями данных.
Таким образом,
ChainMap полезен в ситуациях, когда нужно комбинировать несколько наборов
данных и иметь быстрый доступ ко всей информации сразу, не прибегая к
физическому слиянию словарей.
Курс по стандартной библиотеке: https://stepik.org/a/259466?utm_source=proproprogs