ChainMap — Búsqueda en varios diccionarios¶
La clase ChainMap
maneja una secuencia de diccionarios, y busca a través de
ellos en el orden en que se les da para encontrar valores asociados con llaves.
Un ChainMap
hace un buen contenedor de «contexto», ya que se puede tratar
como una pila para la cual ocurren cambios a medida que la pila crece, con estos
cambios siendo descartados nuevamente a medida que la pila se reduce.
Accediendo a valores¶
El ChainMap
admite la misma interfaz que un diccionario normal para acceder
a los valores existentes
import collections
a = {'a': 'A', 'c': 'C'}
b = {'b': 'B', 'c': 'D'}
m = collections.ChainMap(a, b)
print('Individual Values')
print('a = {}'.format(m['a']))
print('b = {}'.format(m['b']))
print('c = {}'.format(m['c']))
print()
print('Keys = {}'.format(list(m.keys())))
print('Values = {}'.format(list(m.values())))
print()
print('Items:')
for k, v in m.items():
print('{} = {}'.format(k, v))
print()
print('"d" in m: {}'.format(('d' in m)))
Las asignaciones de elementos secundarios se buscan en el orden en que se pasan
al constructor, por lo que el valor informado para la clave 'c'
proviene del
diccionario a
.
$ python3 collections_chainmap_read.py
Individual Values
a = A
b = B
c = C
Keys = ['c', 'b', 'a']
Values = ['C', 'B', 'A']
Items:
c = C
b = B
a = A
"d" in m: False
Reordenando¶
El ChainMap
almacena la lista de asignaciones sobre las cuales busca en una
lista en su atributo maps
. Esta lista es mutable, por lo que es posible
agregar nuevas asignaciones directamente o cambiar el orden de los elementos
para controlar el comportamiento de búsqueda y actualización.
import collections
a = {'a': 'A', 'c': 'C'}
b = {'b': 'B', 'c': 'D'}
m = collections.ChainMap(a, b)
print(m.maps)
print('c = {}\n'.format(m['c']))
# reverse the list
m.maps = list(reversed(m.maps))
print(m.maps)
print('c = {}'.format(m['c']))
Cuando se invierte la lista de asignaciones, el valor asociado con 'c'
cambia.
$ python3 collections_chainmap_reorder.py
[{'c': 'C', 'a': 'A'}, {'c': 'D', 'b': 'B'}]
c = C
[{'c': 'D', 'b': 'B'}, {'c': 'C', 'a': 'A'}]
c = D
Actualizando valores¶
Un ChainMap
no almacena en caché los valores en las asignaciones de
elementos secundarios. Por lo tanto, si se modifican sus contenidos, los
resultados se reflejan cuando el ChainMap
es accedido.
import collections
a = {'a': 'A', 'c': 'C'}
b = {'b': 'B', 'c': 'D'}
m = collections.ChainMap(a, b)
print('Before: {}'.format(m['c']))
a['c'] = 'E'
print('After : {}'.format(m['c']))
Cambiar los valores asociados con las claves existentes y agregar nuevos elementos funciona de la misma manera.
$ python3 collections_chainmap_update_behind.py
Before: C
After : E
También es posible establecer valores a través del ChainMap
directamente,
aunque solo el primer mapeo en la cadena es en realidad modificado.
import collections
a = {'a': 'A', 'c': 'C'}
b = {'b': 'B', 'c': 'D'}
m = collections.ChainMap(a, b)
print('Before:', m)
m['c'] = 'E'
print('After :', m)
print('a:', a)
Cuando el nuevo valor se almacena usando m
, el mapeo a
es actualizado.
$ python3 collections_chainmap_update_directly.py
Before: ChainMap({'c': 'C', 'a': 'A'}, {'c': 'D', 'b': 'B'})
After : ChainMap({'c': 'E', 'a': 'A'}, {'c': 'D', 'b': 'B'})
a: {'c': 'E', 'a': 'A'}
ChainMap
proporciona un método conveniente para crear una nueva instancia
con un mapeo adicional al frente de la lista maps
para que sea fácil
evitar la modificación de las estructuras de datos subyacentes existentes.
import collections
a = {'a': 'A', 'c': 'C'}
b = {'b': 'B', 'c': 'D'}
m1 = collections.ChainMap(a, b)
m2 = m1.new_child()
print('m1 before:', m1)
print('m2 before:', m2)
m2['c'] = 'E'
print('m1 after:', m1)
print('m2 after:', m2)
Este comportamiento de apilamiento es lo que hace que sea conveniente usar
instancias ChainMap
como contextos de plantilla o aplicación.
Específicamente, es fácil de agregar o actualizar valores en una iteración,
luego descartar los cambios para la próxima iteración.
$ python3 collections_chainmap_new_child.py
m1 before: ChainMap({'c': 'C', 'a': 'A'}, {'c': 'D', 'b': 'B'})
m2 before: ChainMap({}, {'c': 'C', 'a': 'A'}, {'c': 'D', 'b':
'B'})
m1 after: ChainMap({'c': 'C', 'a': 'A'}, {'c': 'D', 'b': 'B'})
m2 after: ChainMap({'c': 'E'}, {'c': 'C', 'a': 'A'}, {'c': 'D',
'b': 'B'})
Para situaciones donde el nuevo contexto es conocido o construido de antemano,
también es posible pasar un mapeo a new_child()
.
import collections
a = {'a': 'A', 'c': 'C'}
b = {'b': 'B', 'c': 'D'}
c = {'c': 'E'}
m1 = collections.ChainMap(a, b)
m2 = m1.new_child(c)
print('m1["c"] = {}'.format(m1['c']))
print('m2["c"] = {}'.format(m2['c']))
Este es el equivalente de
m2 = collections.ChainMap(c, *m1.maps)
y produce
$ python3 collections_chainmap_new_child_explicit.py
m1["c"] = C
m2["c"] = E