Diccionarios: dict

Los diccionarios en Python son una estructura de datos preparadas para almacenar colecciones de elementos (separados por coma) compuestos de una clave y un valor (suele llamarlos en ingles key, value). Los diccionarios se delimitan con llaves {}.

Veamos un ejemplo:

persona = {
    "nombre": "Luis",
    "apellido": "Colon",
    "edad": 32
}
type(persona)
# devuelve <class 'dict'>

Los valores pueden accederse directamente desde sus claves. Esto es así tanto para leer como para modificar cada elemento. Las claves son únicas, no puede haber dos elementos con la misma clave.

persona = {
    "nombre": "Luis",
    "apellido": "Colon",
    "edad": 32
}

print(f'Nombre: {persona["nombre"]} {persona["apellido"]} tiene {persona["edad"]} años')
'Luis Colon tiene 32 años'

print(persona["nombre"])
'Luis'

edad = persona["edad"]
print(edad)
32

persona["apellido"] = "Gonzalez"
print(persona)
{"nombre": "Luis", "apellido": "Gonzalez", "edad": 32}

En algunas ocasiones no vamos a tener certeza de que las claves a las que queremos acceder existen.

persona = {
    "nombre": "Luis",
    "apellido": "Colon",
    "edad": 32
}

# La siguiente línea lanzará un error (la clave no existe)
persona["clave_que_no_exite"]

# Para acceder a elementos de los que no sabemos si su clave existe,
# usamos la función "get" de los diccionarios
persona.get("clave_que_no_exite")
# Si la clave (key) no existe la anterior línea no va a lanzar
# un error. Simplemente devolverá None (es el "nada" de Python)

# Si queremos un valor predeterminado para cuando la clave no
# esta definida podemos usar el segundo parámetro opcional de "get"
persona.get("clave_que_no_exite", "valor_predeterminado")

# Tambien es posible "preguntar" si una clave existe
if "clave_buscada" in persona:
    print("clave_buscada SI existe como clave en 'persona'")

# Esto es posible porque los diccionarios definen un iterador con la
# lista de sus claves

for key in persona:
    print(f"Clave en persona: {key}")

Cada par de clave-valor es denominado como elemento (item). La función items de los diccionarios devuleve un objeto iterable que podemos navegar con for y que devuelve en cada paso una clave y un valor.

persona = {
    "nombre": "Juan",
    "apellido": "Colon",
    "edad": 32
}

for k, v in persona.items():
    print(f'Item encontrado: Key:{k}, Value: {v}')

# Item encontrado: Key:nombre, Value: Juan
# Item encontrado: Key:apellido, Value: Colon
# Item encontrado: Key:edad, Value: 32

Los valores puede ser de cualquier tipo e incluso conformar estructuras muy complejas.

Ejemplo:

persona = {
    "nombre": "Luis",
    "apellido": "Colon",
    "edad": 32,
    "estudios": {
        "primario": True,
        "secundario": True,
        "terciario": False,
        "universitario": False
    },
    "experiencia_laboral": {
        "2005-2008": {
            "empresa": "Ferreteria el cosito del coso",
            "cargo": "Vendedor",
            "sueldo": 45000,
            "tareas": ["atender publico", "compras"]
            },
        "2009-2011": {
            "empresa": "Escuela Tecnica San Martin",
            "cargo": "profesor",
            "sueldo": 75000,
            "tareas": ["dictar clases", "elaborar cursos"]
            }
    }
}

En el ejemplo anterior persona["estudios"] es a su vez un diccionario por lo que tiene a su ves las propiedades de un nuevo objeto de este tipo. Las siguientes lineas son válidas para la estructura recién definida.

print(persona["estudios"]["primario"])
True

if persona["estudios"]["secundario"]:
    print('La persona termino el secundario')

En el ejemplo anterior persona["experiencia_laboral"]["2005-2008"]["tareas"] es a su vez una lista por lo que tiene a su ves las propiedades de estas. Las siguientes lineas son válidas para la estructura recién definida.

for tareas in persona["experiencia_laboral"]["2005-2008"]["tareas"]:
    print(f"Tarea: {tarea}")

También es posible usar dict() para crear un diccionario.

d3 = dict(
    nombre='Laura',
    edad=47,
    documento=221029489
)
type(d3)
# muestra <class 'dict'>
print(d3)
{'nombre': 'Laura', 'edad': 47, 'documento': 221029489}

No confundir diccionarios con set

En Python tambien se usan las llaves {} para definir conjuntos (sets).

Por ejemplo s = {1, 2, 3, 4, 5} no es un dictionario, es un set y type(s) mostrará <class 'set'>

Los sets son listas no ordenadas de elementos únicos (los duplicados se eliminan automáticamente).

Tareas

  • Hacer un PR con una propuesta de solución para el ejercicio 020

"""
Completar la funcion para que devuelva la "frase" pasada como parámetro
reemplazadas todas sus vocales con la "a" (o cualquier otra "vocal" que se
pase como parámetro)
"""


def cambia_vocales(frase, vocal="a"):
    pass


# ------------------------------------------------------------------------
# NO BORRAR O MODIFICAR LAS LINEAS QUE SIGUEN
# ------------------------------------------------------------------------
# Una vez terminada la tarea ejecutar este archivo.
# Si se ve la leyenda 'Ejercicio terminado OK' el ejercicio se considera completado.
# La instruccion "assert" de Python lanzará un error si lo que se indica a
#   continuacion es falso.
# Si usas GitHub (o similares) podes hacer una nueva rama con esta solución,
#   crear un "pull request" y solicitar revision de un tercero.

assert cambia_vocales("hola") == "hala"
assert cambia_vocales("Juan Carlos") == "Jaan Carlas"
assert cambia_vocales("Pepito", "e") == "Pepete"
assert cambia_vocales(vocal="i", frase="me llamo juan") == "mi llimi jiin"

# revisar mayúsculas y minúsculas
assert cambia_vocales("HOli") == "HAla"

print('Ejercicio terminado OK')
  • Hacer un PR con una propuesta de solución para el ejercicio 030

"""
La siguiente funcion busca contar cuantas veces se repite cada palabra.
Por ejemplo de "Hola Juan. Hola Pedro" devuelve {"Hola": 2, "Juan.": 1, "Pedro": 1}
Tarea: Corregir el error de la función para que devuelva el resultado correcto.
Se espera que los signos de puntuacion no afecten el resultado y que las mayusculas
y minusculas no cuenten como palabras diferentes.
"""


def contar_palabras(frase):
    """ 
    Esta funcion toma una frase y devuelve un diccionario
    con una llave por cada palabra y un valor igual a la
    cantidad de veces que aparece en la frase
    """
    palabras = frase.split()
    resultados = {}
    for palabra in palabras:
        if palabra in resultados.keys():
            # si existe, sumarle 1
            resultados[palabra] += 1
        else:
            # si no existe, inicializarla
            resultados[palabra] = 0
    return resultados


# ------------------------------------------------------------------------
# NO BORRAR O MODIFICAR LAS LINEAS QUE SIGUEN
# ------------------------------------------------------------------------
# Una vez terminada la tarea ejecutar este archivo.
# Si se ve la leyenda 'Ejercicio terminado OK' el ejercicio se considera completado.
# La instruccion "assert" de Python lanzará un error si lo que se indica a
#   continuacion es falso.
# Si usas GitHub (o similares) podes hacer una nueva rama con esta solución,
#   crear un "pull request" y solicitar revision de un tercero.


f1 = contar_palabras("Hola dijo Juan. Hola dijo pedro")
assert f1['Hola'] == 2

himno = "Oid mortales el grito sagrado. Libertad, libertad, libertad. Oid el ruido de rotas cadenas"
palabras_himno = contar_palabras(himno)
assert palabras_himno['Oid'] == 2
assert palabras_himno['libertad'] == 3

print('Ejercicio terminado OK')
  • Hacer un PR con una propuesta de solución para el ejercicio 031

"""
La siguiente funcion toma como parámetro una lista de diccionarios
y cuenta la cantidad de elementos que tiene un valor especifico en
una propiedad definida.
Por ejemplo de la lista
lista = [
    {"genero": "M", "nombre": "Juan"},
    {"genero": "F", "nombre": "Pablo"},
    {"genero": "F", "nombre": "Juana", "apellido": "Gomez"},
    {"genero": "M", "nombre": "Victor"},
    {"genero": "M", "nombre": "Juan Pablo", "apellido": "Velez"},
    {"genero": "F", "nombre": "Juana"},
    {"genero": "F", "nombre": "Victoria"}
]
Se esperan estos posibles resultados

contar_si(lista, "genero", "M") == 3
contar_si(lista, "genero", "F") == 4
contar_si(lista, "nombre", "Juana") == 2

pero la funcion da error en algunos casos como
contar_si(lista, "apellido", "Gomez")
donde en realidad esperamos que devuelva 1

Tarea: Mejorar la función para que no de errores cuando una clave no existe
"""


def contar_si(lista, propiedad, valor):
    """ 
    Esta funcion cuenta la cantidad de elementos (diccionarios) en "lista"
    que tiene una "propiedad" con un "valor" específico.
    """
    contador = 0
    for elemento in lista:
        if elemento[propiedad] == valor:
            contador += 1

    return contador

# ------------------------------------------------------------------------
# NO BORRAR O MODIFICAR LAS LINEAS QUE SIGUEN
# ------------------------------------------------------------------------
# Una vez terminada la tarea ejecutar este archivo.
# Si se ve la leyenda 'Ejercicio terminado OK' el ejercicio se considera completado.
# La instruccion "assert" de Python lanzará un error si lo que se indica a
#   continuacion es falso.
# Si usas GitHub (o similares) podes hacer una nueva rama con esta solución,
#   crear un "pull request" y solicitar revision de un tercero.


lista = [
    {"genero": "M", "nombre": "Juan"},
    {"genero": "F", "nombre": "Pablo"},
    {"genero": "F", "nombre": "Juana", "apellido": "Gomez"},
    {"genero": "M", "nombre": "Victor"},
    {"genero": "M", "nombre": "Juan Pablo", "apellido": "Velez"},
    {"genero": "F", "nombre": "Juana"},
    {"genero": "F", "nombre": "Victoria"}
]

assert contar_si(lista, "genero", "M") == 3
assert contar_si(lista, "genero", "F") == 4
assert contar_si(lista, "nombre", "Juana") == 2
assert contar_si(lista, "apellido", "Gomez") == 1
assert contar_si(lista, "apellido", "Perez") == 0

print('Ejercicio terminado OK')
  • Hacer un PR con una propuesta de solución para el ejercicio 032

"""
El siguiente código permite crear facturas de ventas y agregar items.
El código no tiene fallas pero el cliente desea que la lista de items
no tenga productos duplicados. Si se intenta agregar un producto por
segunda vez, la función deberia darse cuenta y actualizar los valores
de ese item y no agregarlo como nuevo item.
"""


def agregar_item(factura, producto, precio_unitario, cantidad=1):
    """
    Agregar un item a una factura que se pasa como parámetro
    """
    precio_total = cantidad * precio_unitario
    item = {
        'producto': producto,
        'precio_unitario': precio_unitario,
        'cantidad': cantidad,
        'precio_total': precio_total,
    }

    factura['items'].append(item)
    factura['total'] += precio_total

def crear_factura():
    """ Crear una factura """
    factura = {
        'total': 0,
        'items': []  # lista de items
    }

    return factura

mi_factura = crear_factura()
agregar_item(mi_factura, 'Alfajor', 150, 3)
agregar_item(mi_factura, 'Turron', 53)
agregar_item(mi_factura, 'Turron', 53)


# ------------------------------------------------------------------------
# NO BORRAR O MODIFICAR LAS LINEAS QUE SIGUEN
# ------------------------------------------------------------------------
# Una vez terminada la tarea ejecutar este archivo.
# Si se ve la leyenda 'Ejercicio terminado OK' el ejercicio se considera completado.
# La instruccion "assert" de Python lanzará un error si lo que se indica a
#   continuacion es falso.
# Si usas GitHub (o similares) podes hacer una nueva rama con esta solución,
#   crear un "pull request" y solicitar revision de un tercero.

assert mi_factura['total'] == 556

items_en_factura = mi_factura['items']
assert len(items_en_factura) == 2

print('Ejercicio terminado OK')
  • Hacer un PR con una propuesta de solución para el ejercicio 041

"""
La funcion "crear_mazo_cartas_espaniolas" funciona casi bien.
Por algun motivo faltan algunas cartas.
La tarea de este ejercicio es reparar esta función para que el mazo este completo
"""


def crear_mazo_cartas_espaniolas():
    palos = ['oro', 'copa', 'espada', 'basto']
    mazo = []
    for n in range(1, 12):
        for palo in palos:
            carta = {'numero': n, 'palo': palo}
            mazo.append(carta)
    return mazo

mazo_esp = crear_mazo_cartas_espaniolas()
print(mazo_esp)

# ------------------------------------------------------------------------
# NO BORRAR O MODIFICAR LAS LINEAS QUE SIGUEN
# ------------------------------------------------------------------------
# Una vez terminada la tarea ejecutar este archivo.
# Si se ve la leyenda 'Ejercicio terminado OK' el ejercicio se considera completado.
# La instruccion "assert" de Python lanzará un error si lo que se indica a
#   continuacion es falso.
# Si usas GitHub (o similares) podes hacer una nueva rama con esta solución,
#   crear un "pull request" y solicitar revision de un tercero.


assert {'numero': 10, 'palo': 'oro'} in mazo_esp
assert {'numero': 11, 'palo': 'oro'} in mazo_esp
assert {'numero': 12, 'palo': 'oro'} in mazo_esp

print('Ejercicio terminado OK')
  • Hacer un PR con una propuesta de solución para el ejercicio 042

"""

La funcion "crear_mazo_cartas_poker" esta incompleta y necesita ser completada para
devolver una lista de diccionarios con todas las cartas disponibles en un mazo de poker.

Nota: El ejercicio 041* ya muestra una función similar que puede usarse como ayuda

* https://github.com/avdata99/programacion-para-no-programadores/blob/master/ejercicios/ejercicio-041/ejercicio.py

"""


def crear_mazo_cartas_poker():
    palos = ['pica', 'trebol', 'corazon', 'diamante']
    mazo = []

    # COMPLETAR la lista con un diccionario por cada carta de
    # la forma {'numero': X, 'palo': Y}
    # El test de la parte inferior de este archivo ayuda a validar
    # el resultado esperado

    return mazo

# ------------------------------------------------------------------------
# NO BORRAR O MODIFICAR LAS LINEAS QUE SIGUEN
# ------------------------------------------------------------------------
# Una vez terminada la tarea ejecutar este archivo.
# Si se ve la leyenda 'Ejercicio terminado OK' el ejercicio se considera completado.
# La instruccion "assert" de Python lanzará un error si lo que se indica a
#   continuacion es falso.
# Si usas GitHub (o similares) podes hacer una nueva rama con esta solución,
#   crear un "pull request" y solicitar revision de un tercero.

mazo_poker = crear_mazo_cartas_poker()

assert {'numero': 9, 'palo': 'pica'} in mazo_poker
assert {'numero': 10, 'palo': 'pica'} in mazo_poker
assert {'numero': 'J', 'palo': 'pica'} in mazo_poker
assert {'numero': 'Q', 'palo': 'pica'} in mazo_poker
assert {'numero': 'K', 'palo': 'pica'} in mazo_poker

assert {'numero': 9, 'palo': 'diamante'} in mazo_poker
assert {'numero': 10, 'palo': 'diamante'} in mazo_poker
assert {'numero': 'J', 'palo': 'diamante'} in mazo_poker
assert {'numero': 'Q', 'palo': 'diamante'} in mazo_poker
assert {'numero': 'K', 'palo': 'diamante'} in mazo_poker

print('Ejercicio terminado OK')

Algunos ejemplos de uso

data = {
    'edad': 32,
    'nombre': 'Juan'
}

nombre = data['nombre']  # equivalente a data.get('nombre)
edad = data['edad']
print(f'Nombre: {nombre}, edad {edad} años')

"""
data['algo-que-no-existe']
# genera un error 
# KeyError: 'algo-que-no-existe'
# mientras que 
data.get('algo-que-no-existe')
devuelve None
"""

# Nombre: Juan, edad 32 años

for k, v in data.items():
    print(f'Item encontrado: Key:{k}, Value: {v}')

# Item encontrado: Key:edad, Value: 32
# Item encontrado: Key:nombre, Value: Juan

data = {
    'edad': 32,
    'nombre': 'Juan',
    'educacion': {
        'secundario': 'Monserrat',
        'universidad': 'UNC',
    }
}

nombre = data['nombre']
edad = data['edad']
universidad = data['educacion']['universidad']

print(f'Nombre: {nombre}, edad {edad} años. Universidad: {universidad}')
# Nombre: Juan, edad 32 años. Universidad: UNC

# Agregarle datos
data['ocupacion'] = 'Desarrollador'
data['educacion']['primario'] = 'San Juan'

print(data)
# {'edad': 32, 'nombre': 'Juan', 'educacion': {'secundario': 'Monserrat', 'universidad': 'UNC', 'primario': 'San Juan'}, 'ocupacion': 'Desarrollador'}

print(data.get('ocupacion'))
# 'Desarrollador'

print(data.get('NO EXISTE'))
# None

print(data.get('NO EXISTE', 'valor predeterminado'))
# valor predeterminado
# lista de diccionarios
data = {
    'personas': [
        {'nombre': 'Juan', 'edad': 20},
        {'nombre': 'Pedro', 'edad': 30},
        {'nombre': 'María', 'edad': 40},
    ]
}

print('PERSONAS:')
for persona in data['personas']:
    nombre = persona['nombre']
    edad = persona['edad']
    print(f' - Nombre: {nombre}, edad {edad} años')

""" resultados

PERSONAS:
 - Nombre: Juan, edad 20 años
 - Nombre: Pedro, edad 30 años
 - Nombre: María, edad 40 años

"""