miércoles, 15 de abril de 2015

Con OrderedDict el orden ha llegado


Un diccionario en Python es un objeto de la clase dict bastante peculiar porque sus pares de claves/valor cuando son recorridos no tienen porque aparecer en el mismo orden en que fueron agregados. Es más, si recorremos un diccionario varias veces y mostramos los pares existentes es muy probable que aparezcan cada vez en distinto orden.

Después de ejecutar tres veces el siguiente ejemplo:
dicc = {'a':1, 'b':2, 'c':3, 'd':4, 'e':5, 'f':6, 'g':7}

for clave in dicc.keys():
    print(clave, "->" , dicc[clave], end="|")

El resultado obtenido ha sido:

1) a -> 1|f -> 6|g -> 7|e -> 5|c -> 3|b -> 2|d -> 4|
2) f -> 6|d -> 4|a -> 1|b -> 2|g -> 7|c -> 3|e -> 5|
3) f -> 6|b -> 2|c -> 3|g -> 7|a -> 1|d -> 4|e -> 5|


El concepto de orden en un diccionario está fuera de lugar. Sin embargo, el objeto OrderedDict del módulo collections pertenece a una subclase de la clase dict que sí permite mantener las claves de un diccionario en el mismo orden en que fueron insertadas. Además, si una nueva entrada sobrescribe una existente la posición de inserción original se mantiene; pero si previamente es eliminada y después se vuelve a agregar pasará a ocupar la última posición.

import collections

escritores = {'Borges':'Argentina', 
              'Coehlo':'Brasil',
              'Lorca':'España', 
              'Márquez':'Colombia',
              'Neruda':'Chile',
              'Rulfo':'México',
              'Saramago':'Portugal'}

# Crear un objeto OrderedDict:
odescritores = collections.OrderedDict(escritores)

# Suprimir par clave/valor por el final y 
# asignar tupla con datos:
escritor_ultimo = odescritores.popitem()

# Muestra tupla:
print(escritor_ultimo)  # ('Saramago', 'Portugal')

# Muestra elementos manteniendo orden original
# sin elemento borrado
print(odescritores)  

# Suprimir par clave/valor por el principio y
# asignar tupla con datos:
escritor_primero = odescritores.popitem(last=False)

# Muestra tupla 
print(escritor_primero) # ('Borges', 'Argentina')

# Muestra elementos manteniendo orden original
# sin elemento borrado
print(odescritores)  

# Suprimir par clave/valor por el final y 
# asignar datos a dos variables:
escritor, pais = odescritores.popitem()

print(escritor, pais)  # Rulfo México

# Muestra elementos manteniendo orden original
# sin elemento borrado 
print(odescritores)

# OrderedDict([('Lorca', 'España'), 
#              ('Coehlo', 'Brasil'),
#              ('Neruda', 'Chile'), 
#              ('Saramago', 'Portugal')])

# Obtener un diccionario a partir de objeto OrderedDict:
diccionario_desordenado = dict(odescritores)

# Los elementos aparecerán a su libre albedrío:
print(diccionario_desordenado)
# {'Lorca': 'España', 'Coehlo': 'Brasil', 
#  'Saramago': 'Portugal', 'Neruda': 'Chile'}

# Mover clave/valor al final del objeto OrderedDict:
# Mueve al final (si existe) el par de clave 'Coehlo'
odescritores.move_to_end('Coehlo')

print(odescritores)
# OrderedDict([('Lorca', 'España'), ('Neruda', 'Chile'),
#              ('Saramago', 'Portugal'), ('Coehlo', 'Brasil')])

# Mover un par clave/valor al principio del objeto OrderedDict:
# Mueve al principio (si existe) el par
odescritores.move_to_end('Coehlo', last=False)

# Muestra datos con par de Coehlo' al comienzo
print(odescritores)

# Añadir nuevos pares al final del objeto OrderedDict:
odescritores['Martí'] = 'Cuba'   # Añadir nuevo par
odescritores['Benedetti'] = 'Uruguay' # Añadir nuevo par

print(odescritores)   
# Muestra: OrderedDict([('Coehlo', 'Brasil'), 
#                       ('Lorca', 'España'), 
#                       ('Neruda', 'Chile'), 
#                       ('Saramago', 'Portugal'), 
#                       ('Martí', 'Cuba'), 
#                       ('Benedetti', 'Uruguay')])

# Obtener lista ordenada de tuplas del objeto OrderedDict:
lista_ordenada = sorted(odescritores.items())

print(lista_ordenada)  
# [('Benedetti', 'Uruguay'), ('Coehlo', 'Brasil'), 
# ('Lorca', 'España'), ('Martí', 'Cuba'), 
# ('Neruda', 'Chile'), ('Saramago', 'Portugal')]

# Obtener lista ordenada de claves del objeto OrderedDict:
lista_claves = sorted(odescritores.keys())
print(lista_claves)  
# ['Benedetti', 'Coehlo', 'Lorca', 
#  'Martí', 'Neruda', 'Saramago']

# Mostrar todos los pares del objeto OrderedDict 
# ordenados por las claves:
for clave in sorted(odescritores.keys()):
    print(clave, odescritores[clave])

# Obtener lista ordenada de valores del objeto OrderedDict:
lista_valores = sorted(odescritores.values())
print(lista_valores)  
# ['Brasil', 'Chile', 'Cuba', 
#  'España', 'Portugal', 'Uruguay']

# Obtener lista ordenada inversa de valores:
lista_valores = sorted(odescritores.values(), reverse=True)
print(lista_valores)  
# ['Uruguay', 'Portugal', 'España', 
# 'Cuba', 'Chile', 'Brasil']

# Agregar a un objeto OrderedDict los elementos de
# otro objeto OrdereDict:
odescritoras = collections.OrderedDict()
odescritoras['Allende'] = 'Chile'
odescritoras['Laforet'] = 'España'
odescritoras['Pizarnik'] = 'Argentina'
odescritoras['Mastretta'] = 'México'
odescritores.update(odescritoras)

Comparando diccionarios y objetos OrderedDict


Si se comparan dos diccionarios el resultado será True cuando contengan los mismos pares de claves/valores sin importar la posición que ocupen:
dicc1 = {'a':1, 'b':2}
dicc2 = {'b':2, 'a':1}
print(dicc1 == dicc2)  # True

En cambio, si se comparan dos objetos OrderedDict el resultado será True cuando contengan los mismos pares de claves/valores y además ocupen la misma posición:

odicc1 = collections.OrderedDict()
odicc1['a'] = 1
odicc1['b'] = 2
odicc2 = collections.OrderedDict()
odicc2['b'] = 2
odicc2['a'] = 1
print(odicc1 == odicc2)  # False


Importante: A partir de Python 3.6 la implementación de los diccionarios ha cambiado, manteniéndose en todo momento el orden en que fueron agregados los elementos a un diccionario cuando son recorridos o consultados. 

Ir al índice del tutorial de Python