Шаг 21.
Python: сборник рецептов.
Структуры данных и алгоритмы. Объединение нескольких отображений в одно

    На этом шаге мы рассмотрим основные возможности класса collections.ChainMap.

Задача

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

Решение

    Предположим, у вас есть два словаря:

a = {'x': 1, 'z': 3} 
b = {'y': 2, 'z': 4}

    А теперь предположим, что вы хотите провести поиски, в ходе которых вам нужно проверить оба словаря (то есть сначала проверить в словаре а, а потом в b, если в первом словаре искомое не найдено). Простой способ сделать это - использовать класс ChainMap из модуля collections. Например:

from collections import ChainMap 

c = ChainMap(a, b) 
print(c['x']) # Выводит 1 (из a) 
print(c['y']) # Выводит 2 (из b) 
print(c['z']) # Выводит 3 (из a)


Обсуждение

    ChainMap принимает несколько отображений и делает их логически единым целым. Однако в буквальном смысле они не сливаются. Вместо этого ChainMap просто содержит список отображений и переопределяет обычные операции над словарями для сканирования данного списка. Большинство операций работает. Например:

>>> len(c)
3
>>> list(c.keys())
['y', 'z', 'x']
>>> list(c.values())
[2, 3, 1]
>>>

    В случае появления одинаковых ключей будут использованы значения из первого словаря. Например, c['z'] в примере всегда будет ссылаться на значение из словаря а, а не из b.

    Операции, которые изменяют отображение, всегда действуют на первое отображение в списке. Например:

>>> c['z'] = 10
>>> c['w'] = 40
>>> del c['x']
>>> a
{'z': 10, 'w': 40}
>>> del c['y']
Traceback (most recent call last):
  .   .   .   .
KeyError: "Key not found in the first mapping: 'y'"
>>>

    ChainMap особенно полезны для работы со значениями, принадлежащими областям видимости, такими как переменные языка программирования (глобальные, локальные и т. п.). На самом деле даже существуют методы, которые все упрощают:

>>> values = ChainMap()
>>> values['x'] = 1
>>> # Добавляем новое отображение
>>> values = values.new_child()
>>> values['x'] = 2
>>> # Добавляем новое отображение
>>> values = values.new_child()
>>> values['x'] = 3
>>> values
ChainMap({'x': 3}, {'x': 2}, {'x': 1})
>>> values['x']
3
>>> # Удаляем последнее отображение
>>> values = values.parents
>>> values['x']
2
>>> # Удаляем последнее отображение
>>> values = values.parents
>>> values['x']
1
>>> values
ChainMap({'x': 1})
>>>

    В качестве альтернативы ChainMap вы можете обдумать слияние словарей с использованием метода update(). Например:

>>> a = {'x': 1, 'z': 3}
>>> b = {'y': 2, 'z': 4}
>>> merged = dict(b)
>>> merged.update(a)
>>> merged['x']
1
>>> merged['y']
2
>>> merged['z']
3
>>> 

    Это работает, но требует от вас создания полностью нового объекта словаря (или необратимого изменения одного из существующих). В этом случае при изменении одного из первоначальных словарей изменения не затронут новый объект объединенного словаря. Например:

>>> a['x'] = 13
>>> merged['x']
1
>>> 

    ChainMap использует первоначальные словари, поэтому не подвержен такому поведению. Например:

>>> a = {'x': 1, 'z': 3}
>>> b = {'y': 2, 'z': 4}
>>> merged = ChainMap(a, b)
>>> merged['x']
1
>>> a['x'] = 42
>>> merged['x'] # Изменение происходит и в объединенных словарях
42
>>> 

    Со следующего шага мы начинаем рассматривать строки и текст.




Предыдущий шаг Содержание Следующий шаг