Mostrando entradas con la etiqueta Colecciones de datos. Mostrar todas las entradas
Mostrando entradas con la etiqueta Colecciones de datos. Mostrar todas las entradas

viernes, 7 de agosto de 2026

LENGUAJES. Python

Diccionarios complejos (II)

Colecciones de datos

En un diccionario complejo el value (par clave:valor) de un posible "campo" puede estar formado por una lista de datos ([]), de modo que el tratamiento de esos datos haga encesario implementar estrategias específicas. Sin pretender ser exhaustivos, ese tratamiento incluye operaciones como conteo, visualización y consulta (de toda la lista o de un elemento), añadido, supresión o modificación... En esta entrada estudiaremos cómo realizar estas operaciones.

Ya que por algún punto hay que empezar, nos situaremos en el siguiente contexto: hemos definido la estructura de diccionario...



 "ALU-cod-n": {
        "nombre": "al_nombre",
        "curso": "al_curso",
        "nee": true,
        "medidas_apoyo": [
            "mad_1"
        ],
        "intervenciones": [
            {
                "fecha": "int_aa_mm_dd",
                "motivo": "int_mot",
                "acuerdos": "int_acuerdos"
            }
        ]
    },


... ya sabemos cómo añadir elementos a los "campos" simples nombre - curso - nee...



import json
import os

def solicitar_booleano(mensaje):
    """
    Solicita una respuesta booleana al usuario garantizando que la entrada sea s/n.
    """
    while True:
        respuesta = input(f"{mensaje} (s/n): ").strip().lower()
        if respuesta in ['s', 'si', 'sí']:
            return True
        elif respuesta in ['n', 'no']:
            return False
        print(" [!] Entrada no válida. Por favor, introduzca 's' para Sí o 'n' para No.")

def capturar_ficha(codigo_num):
    cod_alumno = f"ALU-cod-{codigo_num}"
    print(f"\n==========================================")
    print(f"  NUEVA FICHA DE EXPEDIENTE: {cod_alumno}")
    print(f"==========================================")
    
    print("\n--- AVISO DE FORMATO DE ENTRADA ---")
    print("  * Nombre del alumno: Apellidos, Nombre (ejemplo: García López, María)")
    print("  * Curso: Etapa y grupo (ejemplo: 2º ESO B  o  1º Bachillerato A)")
    print("-------------------------------------\n")
    
    nombre = input("Nombre completo del alumno (ej: García López, María): ").strip()
    curso = input("Curso y grupo (ej: 2º ESO B): ").strip()
    nee = solicitar_booleano("¿Presenta Necesidades Educativas Especiales (NEE)?")
    

... pero todavía no sabemos cómo introducir datos en el "campo" "medidas_apoyo": [] cuyo value es una lista, como podemos identificar por el uso corchetes [] como delimitadores. El script que sigue cubre esta necesidad, aunque tiene una funcionalidad limitada.



import json
import os


def solicitar_booleano(mensaje):
    """Solicita una respuesta booleana al usuario garantizando que la entrada sea s/n."""
    while True:
        respuesta = input(f"{mensaje} (s/n): ").strip().lower()
        if respuesta in ["s", "si", "sí"]:
            return True
        elif respuesta in ["n", "no"]:
            return False
        print(
            " [!] Entrada no válida. Por favor, introduzca 's' para Sí o 'n' para No."
        )


# --- FUNCIÓN Cumplimentar datos de medidas_apoyo ---------------------------------------

def capturar_medidas_apoyo():
    """Captura de forma interactiva la lista de medidas de apoyo."""
    medidas = []
    quiere_anadir = solicitar_booleano(
        "¿Desea registrar alguna medida de apoyo?"
    )

    contador = 1
    while quiere_anadir:
        medida = input(f" -> Ingrese la medida de apoyo #{contador}: ").strip()
        if medida:
            medidas.append(medida)
            contador += 1
        quiere_anadir = solicitar_booleano("¿Desea añadir otra medida de apoyo?")

    return medidas


# --- FUNCIÓN PRINCIPAL de ejecución. -----------------------------------------------

def capturar_ficha(codigo_num):
    cod_alumno = f"ALU-cod-{codigo_num}"
    print(f"\n==========================================")
    print(f"  NUEVA FICHA DE EXPEDIENTE: {cod_alumno}")
    print(f"==========================================")

    print("\n--- AVISO DE FORMATO DE ENTRADA ---")
    print(
        "  * Nombre del alumno: Apellidos, Nombre (ejemplo: García López, María)"
    )
    print(
        "  * Curso: Etapa y grupo (ejemplo: 2º ESO B  o  1º Bachillerato A)"
    )
    print("-------------------------------------\n")

    nombre = input("Nombre completo del alumno (ej: García López, María): ").strip()
    curso = input("Curso y grupo (ej: 2º ESO B): ").strip()
    nee = solicitar_booleano(
        "¿Presenta Necesidades Educativas Especiales (NEE)?"
    )

    # 1. Llamamos a la nueva utilidad para generar la lista
    medidas_apoyo = capturar_medidas_apoyo()

    # 2. Retornamos el diccionario completo con el nuevo campo
    registro = {
        "codigo_alumno": cod_alumno,
        "nombre": nombre,
        "curso": curso,
        "nee": nee,
        "medidas_apoyo": medidas_apoyo,
    }

    return registro

# ==============================================================================
# BLOQUE DE EJECUCIÓN PRINCIPAL
# ==============================================================================
if __name__ == "__main__":
    # Solicitamos o asignamos el número identificador para el código de alumno
    num_expediente = 1

    # Llamada a la función principal para capturar los datos interactivos
    expediente_alumno = capturar_ficha(num_expediente)

    # Mostramos el resultado por pantalla con formato JSON legible
    print("\n==========================================")
    print("      REGISTRO GENERADO CON ÉXITO")
    print("==========================================")
    print(json.dumps(expediente_alumno, indent=4, ensure_ascii=False))


Este script complementa el de creación de registro añadiendo, a partir de def capturar_medidas_apoyo(): le procedimiento para obtener los primeros posibles datos de medidas_apoyo. Esta función es llamada desde la función principal def capturar_ficha(codigo_num): y se basa, en lo fundamental, en el bucle while quiere_anadir: que permite al usuario introducir cuantas medidas de apoyo desee.

Resuelto el procedimiento de creación de datos, nos situamos ahora en el segundo contexto: ya disponemos de un archivo JSON que contiene datos suficientes como para que sea pertinente realizar diferentes operaciones con la información de medidas_apoyo de los distintos registros-diccionarios.

En este contexto empezamos por acceder al archivo JSON para trabajar con él desde el script (en memoria) como diccionario complejo. Este procedimiento ya nos es conocido.



import json

# Ruta absoluta al archivo JSON
ruta_archivo = r"C:\MisDatosPropios\exped_orienta.JSON"

# 1. Cargar el contenido del archivo JSON en memoria
with open(ruta_archivo, "r", encoding="utf-8") as archivo:
    expedientes = json.load(archivo)
    

De las entradas anteriores sabemos crear el listado las claves principales, acceder al contenido de un archivo concreto o a todos ellos. También sabemos manipular el contenido de deteminados campos. Ahora nos detendremos en el acceso y las posibles modificaciones del contenido del campo cuyo value es una lista. Empezaremos por obtener el listado completo de registros mostrando cuántas y cuales son las medidas educativas en cada uno de ellos.



import json

# Ruta absoluta al archivo JSON
ruta_archivo = r"C:\MisDatosPropios\exped_orienta.JSON"

try:
    # 1. Cargar el contenido del archivo JSON en memoria
    with open(ruta_archivo, "r", encoding="utf-8") as archivo:
        expedientes = json.load(archivo)

    print("\n==================================================")
    print("    LISTADO COMPLETO DE EXPEDIENTES Y MEDIDAS     ")
    print("==================================================\n")

    # Adaptación dinámica según si el JSON es una lista [...] o un diccionario {...}
    registros = (
        expedientes.items()
        if isinstance(expedientes, dict)
        else enumerate(expedientes, 1)
    )

    for id_registro, datos in registros:
        # Obtenemos la clave principal (clave del dict o campo 'codigo_alumno')
        if isinstance(datos, dict):
            clave_principal = datos.get("codigo_alumno", f"Registro #{id_registro}")
            medidas = datos.get("medidas_apoyo", [])
        else:
            clave_principal = str(id_registro)
            medidas = datos if isinstance(datos, list) else []

        # Contamos la cantidad total de elementos en la lista
        total_medidas = len(medidas)

        # Imprimimos la salida formateada en CMD
        print(f"📌 Clave Principal: {clave_principal}")
        print(f"   Total de medidas educativas aplicadas: {total_medidas}")

        if total_medidas > 0:
            print("   Listado de medidas:")
            for i, medida in enumerate(medidas, 1):
                print(f"      {i}. {medida}")
        else:
            print("   Listado de medidas: (Sin medidas registradas)")

        print("-" * 50)

except FileNotFoundError:
    print(f" [!] Error: No se encontró el archivo en la ruta '{ruta_archivo}'.")
except json.JSONDecodeError:
    print(
        " [!] Error: El archivo JSON no tiene un formato válido o está corrupto."
    )


Podríamos dirigir nuestra atención al análisis del contenido de este "campo", pero dejaremos estos procedimientos para una tercera fase del estudio de los diccionarios y nos atendremos a explicar los procedimientos de manejo y modificación del value lista, si bien no es muy diferente del del que ya estudiamos en el tratamiento de estas colecciones de datos.

Al tratarse de un diccionario ya desarrollado en cuanto a su contenido, lo primero que deberemos hacer es identificar y acceder al registro de nuestro interés. Para ello debemos usar la clave principal /(vg. "ALU-2024-01") y, a partir de ahí a la clave "medidas_apoyo".

Disponemos de dos procedimientos para acceder a los datos de la lista. Bien directamente identificando al alumno por su clave:



import json

# Ruta absoluta al archivo JSON
ruta_archivo = r"C:\MisDatosPropios\exped_orienta.JSON"

# 1. Cargar el contenido del archivo JSON en memoria
with open(ruta_archivo, "r", encoding="utf-8") as archivo:
    expedientes = json.load(archivo)

# 2. Identificamos directamente al alumno por su clave

id_alumno = "ALU-2024-01"

# 3. Verificar existencia y mostrar las medidas elemento por elemento
if id_alumno in expedientes:
    print(f"Medidas de apoyo para {id_alumno}:")
    for medida in expedientes[id_alumno]["medidas_apoyo"]:
        print(f"- {medida}")



... o bien hacerlo de forma interactiva introducciendo por input() el identificador de su clave, suponiendo que tenemos acceso a un listado de claves y nombres que nos facilitan la identificación del sujeto deseado.



import json

# Ruta absoluta al archivo JSON
ruta_archivo = r"C:\MisDatosPropios\exped_orienta.JSON"

# 1. Cargar el contenido del archivo JSON en memoria
with open(ruta_archivo, "r", encoding="utf-8") as archivo:
    expedientes = json.load(archivo)
    
# 2. Listado de claves-sujetos

listado = [f"{clave} - {datos['nombre']}" for clave, datos in expedientes.items()]
for al in listado:
    print(al)

# 3. Solicitar la clave directamente al usuario
clave = input("Introduce la clave del expediente: ")

# 4. Acceder al valor del campo 'medidas_apoyo' del registro indicado
medidas = expedientes[clave]["medidas_apoyo"]

# 5. Mostrar el contenido actual
print("Medidas de apoyo actuales:", medidas)


Una vez que accedemos al listado de medidas, podemos limitarnos a mostrarlo, pero también podemos actuar sobre él. Veamos algunas acciones posibles. Se escriben a continuación del script anterior).



print("\n--- ACCIONES SOBRE LAS MEDIDAS ---")

# (1) Conocer el número de medidas
print(f"\n4. Número total de medidas registradas: {len(medidas)}")

# (2) Añadir una nueva medida
nueva = input("1. Introduce la nueva medida a añadir: ").strip()
medidas.append(nueva)
print("   Lista actualizada:", medidas)

# (3) Borrar una medida
a_borrar = input("\n2. Introduce la medida a eliminar: ").strip()
medidas.remove(a_borrar)
print("   Lista tras borrar:", medidas)

# (4) Ordenar alfabéticamente las medidas
medidas.sort()
print("\n3. Medidas ordenadas alfabéticamente:", medidas)

# (5) Saber si existe una determinada medida
buscar = input("\n5. Consulta si existe una medida concreta: ").strip()
print(f"   -> ¿Existe '{buscar}' en la lista?: {buscar in medidas}")


Lo suyo sería actualizar el archivo JSON, pero por ahora no es necesario ya que simplmente estamos aprendiendo a trabajar con los componentes del diccionario.

martes, 4 de agosto de 2026

LENGUAJES. Python

Diccionarios complejos (I)

Colecciones de datos

En las dos entradas previas en las que hemos trabajado con diccionarios hemos empleado de forma intencional formulaciones simples de esta colección de datos. Se trataba de comprender en qué consistía y cómo se podía trabajar con ella, así que la simplicidad se imponía como necesidad. Pero la realidad es que un diccionario "normal" no suele ser tan sencillo ni su virtud o interés requiere de que los sea.

En esta entrada idearemos una estructura compleja (y más realista, incluso) de un diccionario y trabajaremos con ella. Se trata de comprobar la realidad y la utilidad real de un diccionario. Para ello vamos a plantearlo como colección de datos potencialmente aplicable a un proyecto real de trabajo (como SEO) aunque ajustado a las necesidades didácticas del momento.

Imaginemos que sobre una estructura de diccionario queremos construir un recurso de seguimiento del alumnado. Se trata de una forma de expediente, muy parecido a lo que se podría dar soporte con una base de datos, pero también registrar y mantener como un documento JSON. Algo como esto:



 "ALU-cod-n": {
        "nombre": "al_nombre",
        "curso": "al_curso",
        "nee": true,
        "medidas_apoyo": [
            "mad_1"
        ],
        "intervenciones": [
            {
                "fecha": "int_aa_mm_dd",
                "motivo": "int_mot",
                "acuerdos": "int_acuerdos"
            }
        ]
    },


Muestro aquí la estructura de un elemento del documento CSV sin contenidos ni reales ni supuestos (eso viene después) para que podemos observar en qué consiste la complejidad de la que estamos hablando cuando lo trasladamos a diccionario (ya sabemos que estructura y contenido se mantienen): ahora cada elemento del diccionario - aquí "ALU-cod-n": - es la clave de un value que es a su vez un diccionario, de ahí que el conjunto se pueda considerar como un diccionario de diccionarios.

Ese value además de ser un diccionario (de ahí que todo él se encuentre delimitado por {}, es un diccionario complejo ya que sus diferentes elementos-campos lo son de diferente tipología:

  • Tenemos contenidos-campos clave:valor simples (y de distinto tipo de datos), como "nombre": "al_nombre"
  • Pero también un contenido-campo cuyo value es una lista "medidas_apoyo": ["mad_1"],
  • Y más aun, un contenido-campo cuyo value es una lista de diccionarios (de la que ahora sólo muestro su primer componente clave:valor por simplificar: "intervenciones": [{"fecha": "int_aa_mm_dd",}]

Sobra decir que debermos conocer cómo tratar a cada subestructura o tipo de componente, tanto como con el conjunto. Empezaremos con ello a continuación.



import json
import os

def solicitar_booleano(mensaje):
    """
    Solicita una respuesta booleana al usuario garantizando que la entrada sea s/n.
    """
    while True:
        respuesta = input(f"{mensaje} (s/n): ").strip().lower()
        if respuesta in ['s', 'si', 'sí']:
            return True
        elif respuesta in ['n', 'no']:
            return False
        print(" [!] Entrada no válida. Por favor, introduzca 's' para Sí o 'n' para No.")

def capturar_ficha(codigo_num):
    cod_alumno = f"ALU-cod-{codigo_num}"
    print(f"\n==========================================")
    print(f"  NUEVA FICHA DE EXPEDIENTE: {cod_alumno}")
    print(f"==========================================")
    
    print("\n--- AVISO DE FORMATO DE ENTRADA ---")
    print("  * Nombre del alumno: Apellidos, Nombre (ejemplo: García López, María)")
    print("  * Curso: Etapa y grupo (ejemplo: 2º ESO B  o  1º Bachillerato A)")
    print("-------------------------------------\n")
    
    nombre = input("Nombre completo del alumno (ej: García López, María): ").strip()
    curso = input("Curso y grupo (ej: 2º ESO B): ").strip()
    nee = solicitar_booleano("¿Presenta Necesidades Educativas Especiales (NEE)?")
    
    # Se mantienen las estructuras de listas para su posterior implementación
    return cod_alumno, {
        "nombre": nombre,
        "curso": curso,
        "nee": nee,
        "medidas_apoyo": [],
        "intervenciones": []
    }

def guardar_en_json(nuevos_expedientes, ruta_salida):
    # Limpieza de comillas iniciales o finales introducidas por el usuario
    ruta_salida = ruta_salida.strip('\"\'')
    ruta_salida = os.path.normpath(ruta_salida)
    
    # 1. Comprobar si el directorio existe y crearlo si no
    directorio = os.path.dirname(ruta_salida)
    if directorio and not os.path.exists(directorio):
        os.makedirs(directorio, exist_ok=True)
        print(f" [+] Directorio creado: {os.path.abspath(directorio)}")
        
    datos_acumulados = {}
    
    # 2. Comprobar si el archivo JSON existe para cargar su contenido previo
    if os.path.exists(ruta_salida):
        try:
            with open(ruta_salida, mode="r", encoding="utf-8") as archivo:
                contenido = archivo.read().strip()
                if contenido:
                    datos_acumulados = json.loads(contenido)
            print(f" [+] Archivo existente detectado. Se añadirán los nuevos registros a los {len(datos_acumulados)} existentes.")
        except Exception as e:
            print(f" [!] Advertencia: No se pudo leer el archivo existente ({e}). Se iniciará un JSON nuevo.")
            datos_acumulados = {}
    else:
        print(" [+] El archivo no existe. Se creará un nuevo archivo JSON.")

    # 3. Anexar/actualizar los registros nuevos sobre el objeto cargado
    datos_acumulados.update(nuevos_expedientes)
    
    # 4. Sobrescribir el archivo con el diccionario actualizado en formato JSON legible
    with open(ruta_salida, mode="w", encoding="utf-8") as archivo:
        json.dump(datos_acumulados, archivo, ensure_ascii=False, indent=4)
        
    return os.path.abspath(ruta_salida)

def main():
    expedientes_sesion = {}
    contador = 1
    
    print("----------------------------------------------------------------")
    print(" SISTEMA DE REGISTRO DE EXPEDIENTES ACADÉMICOS")
    print("----------------------------------------------------------------")
    
    # Bucle de captura continua de fichas
    while True:
        codigo, ficha = capturar_ficha(contador)
        expedientes_sesion[codigo] = ficha
        contador += 1
        
        if not solicitar_booleano("\n¿Desea incorporar la ficha de otro alumno?"):
            break
            
    # Guardado de datos en JSON
    if expedientes_sesion and solicitar_booleano("\n¿Desea guardar los expedientes recopilados en un archivo JSON local?"):
        print("\n=========================================================================")
        print(" INSTRUCCIONES PARA LA RUTA DEL ARCHIVO JSON:")
        print(" 1. NO use comillas al principio ni al final (ejemplo: NO use \"C:\\datos\\alumnos.json\")")
        print(" 2. Puede usar barras inclinadas normales ( / ) o barras invertidas ( \\ ).")
        print(" 3. Asegúrese de incluir la extensión .json al final del nombre.")
        print(" 4. Ejemplos de rutas válidas:")
        print("    - Windows (ruta absoluta): C:/MisDatos/expedientes.json  ó  C:\\MisDatos\\expedientes.json")
        print("    - Carpeta actual (ruta relativa): ./expedientes.json  ó  expedientes.json")
        print("=========================================================================\n")
        
        ruta_archivo = input("Introduzca la ruta y nombre del archivo JSON: ").strip()
        try:
            ruta_final = guardar_en_json(expedientes_sesion, ruta_archivo)
            print(f"\n[OK] Registros guardados con éxito en:\n     {ruta_final}")
        except Exception as e:
            print(f"\n[ERROR] No se pudo guardar el archivo JSON: {e}")
            
    print("\nCierre de sesión del sistema.")

if __name__ == "__main__":
    main()



Aunque el script resultante sea mejorable, ya resulta suficientemente complejo (además de funcional) como para que nos hagamos una idea de qué estamos planteando cuando implementamos un diccionario de este nivel de complejidad. Lo que hemos pretendido es ejemplificar lo que sería la primera y básica implementación de una utilidad de este tipo: obtener del usuario los datos básicos (y simples, se podría añadir) que componen la estructura del diccionario y archivar esa información partiendo desde cero y en formato JSON. Cualquier proyecto de este tipo debe contener estas funcionalidades y ajustarse a la estructura del JSON deseado.

Pero si el contexto primero es correcto (y realista), el que planteo a continuación también lo es. Dando por supuesto el exito del anterior, creamos ahora una utilidad de acceso a los datos. Partimos de que ya tenemos algunos creados y deseamos acceder a ellos, entendiendo que ya hemos recopilado cierto volumen de información, disponible como archivo JSON. Evidentemente los datos que incluímos son inventados (sintéticos, que se dice) y no nos vamos a detener en valorar su ajuste a la realidad ni su interés pedagógico real. Para que lo que aquí te presento te sea de ayuda, proporcionaré acceso al documento "inventado" al final de esta entrada.

Dicho lo anterior, lo que ahora necesitamos es un script que nos permita acceder al documento JSON para trabajar con él en memoria como diccionario.



import json

# Ruta absoluta al archivo JSON
ruta_archivo = r"C:\MisDatosPropios\exped_orienta.JSON"

# 1. Cargar el contenido del archivo JSON en memoria
with open(ruta_archivo, "r", encoding="utf-8") as archivo:
    expedientes = json.load(archivo)

# 2. Obtener la clave y los datos del primer registro
primer_id = list(expedientes.keys())[0]
primer_registro = expedientes[primer_id]

# 3. Mostrar el resultado por pantalla (CMD)
print(f"--- PRIMER REGISTRO: {primer_id} ---")
print(json.dumps(primer_registro, ensure_ascii=False, indent=4))


Dado que estamos trabajando con archivos JSON, lo primero que haremos será importar la biblioteca correspondiente import json para cargar en memoria el archivo expedientes = json.load(archivo) desde su ubicación local ruta_archivo y accedemos a él with open() as archivo. Después obtenemos las claves de todos los archivos en formato lista list(expedientes.keys()) y el contenido del primer registro primer_registro = expedientes[primer_id] y lo mostramos por CMD print(json.dumps(primer_registro, ensure_ascii=False, indent=4)).

Mediante este par de script abordamos las dos maniobras básicas que deberemos realizar con diccionarios, simples o complejos. Pero como hay tarea para rato, parece conveniente finalizar ahora esta entrada.

Archivo de datos (formato JSON). Acceso para descarga desde este enlace. Deberás ajustar la dirección ruta_archivo a la que tú elijas como directorio del archivo que has descargado.

jueves, 30 de julio de 2026

LENGUAJES. Python

Diccionarios (II)

Colecciones de datos

Damos continuidad en esta entrada a la temática iniciada en la anterior sobre los diccionarios Python y sus usos. Ahora trataremos cuestiones básicas de manejo de los diccionarios y su contenido.



#A. Procedimientos directos de creación de diccionarios simples ========================

# 1. Creación estándar con llaves

usuario_ana = {
    "nombre": "Ana",
    "edad": 28,
    "lenguajes": ["Python", "JavaScript"],
    "activo": True
}

# 2. Creación con la función dict()

usuario_carlos= dict(nombre="Carlos", edad=35,lenguajes=["OOo Basic","Python"],activo=True)

#B. Acceso a datos =================================================

# 1. Acceso directo con llaves

print(usuario_ana["nombre"])  # Salida: Ana
print(usuario_carlos["edad"])  #Salida: 35


# 2. Acceso seguro con .get() (evita que el programa falle si la clave no existe)

print(usuario_ana.get("edad"))  # Salida: True
print(usuario_carlos.get("salario", "No especificado"))  # Salida: No especificada

#3. Acceso cuando el valor es una lista 

# Cuando el valor es una lista, sólo con la clave obtenemos como valor la propia lista

print(usuario_ana.get("lenguajes"))  # Salida: ["Python", "JavaScript"]

# 3.1. Usando corchetes. Obtener el primer elemento de la lista (índice 0)

primer_lenguaje = usuario_ana["lenguajes"][0]
print(primer_lenguaje)  # Salida: Python

# 3.2. Usando get(). Obtener el primer elemento con .get()

primer_lenguaje = usuario_carlos.get("lenguajes")[0]
print(primer_lenguaje)  # Salida: OOo Basic

# C. Modificar datos: cambiar, borrar y añadir datos ===============================

# 1. Modificar un valor existente
usuario_ana["edad"] = 29

# 2. Agregar una nueva pareja clave-valor
usuario_ana["ciudad"] = "Madrid"

# 3. Agregar elemento a un valor-lista (ej. lenguajes:[lista_lenguajes]

# 3.1. Agregar un nuevo elemento a la lista 'lenguajes' con append()
usuario_ana["lenguajes"].append("C++")

# (3.1.b) Si no estás seguro de que exista la clave 'lenguajes' usa setdefault() para crearlo y luego añade el elemento con append()
usuario_ana.setdefault("lenguajes", []).append("LUA")

# 3.2. Agregar un nuevo elemento a la lista 'lenguajes' usando .extend() (varios elementos)
usuario_carlos["lenguajes"].extend(["JavaScript", "C++", "PHP"])

# (3.2.b) ...o usando +=
usuario_ana["lenguajes"] += ["OOo Basic"]

print(usuario_carlos["lenguajes"])
print(usuario_ana["lenguajes"])

# 4. Borrar

# 4.1 Eliminar una clave específica con 'del'
del usuario_ana["activo"] #Elimina el par activo:True

# Extraer y eliminar un valor con .pop()
edad = usuario_ana.pop("edad") # Elimina el valor (29) del par "edad":29

# Limpiar todo el diccionario
usuario_carlos.clear() # Elimina todo el diccionario (usuario_carlos)

print(usuario_ana.get("activo", "No especificado")) #con get() evitamos mensaje de error. Devuelve mensaje ("No especificado")
print(edad) #Devuelve el valor del par 'edad':29
print(usuario_ana.get('edad')) #Devuelve None por defecto (ya que no se espifica mensaje)
print (usuario_carlos) # Devuelve un diccionario vacío ({})


Reproduzco en este script la creación de dos diccionarios simples para desarrollar la lógica de posibles manipulaciones sobre un diccionario y sus contenidos. La división del script en cuatro partes y la explicitación como comentarios de cada una de las posibles acciones facilita la comprensión del contenido y agiliza esta explicación complementaria.

Podemos diferenciar tres tipos de actuaciones sobre un diccionario:

  • Los procedimientos de acceso a los datos (sección B)
    • mediante el uso de llaves print(usuario_ana["nombre"]) - Salida: Ana
    • mediante la función get() para controlar el posible error de acceso
    • y el acceso en el caso específico de valores-lista, bien para visualizar la lista completa de valores print(usuario_ana.get("lenguajes")), bien para acceder a uno de ellos en concreto, por ejemplo, print(primer_lenguaje = usuario_ana["lenguajes"][0]), mediante el uso de corchetes
  • La modificación de datos (cambiar o añadir) (sección C del script)
    • mediante procedimientos simples usuario_ana["edad"] = 29 de cambio
    • o usuario_ana["ciudad"] = "Madrid" para añadir clave:valor, ambos basados en el uso de corchetes
    • o más complejos como es el caso de la modificación de valores-listas, que requieren el uso de funciones como append(), (sóla en combinación con setdefault()) para añadir un elemento-valor o la función extend() para añadir varios.
  • Finalmente, en la sección D tratamos el borrado de datos: clave: valor del usuario_ana["activo"], sólo valor edad = usuario_ana.pop("edad") y del diccionario completo usuario_carlos.clear()

Finalizo aquí esta segunda entrada sobre los diccionarios, pero aun hay materia para más...

miércoles, 29 de julio de 2026

LENGUAJES. Python

Diccionarios (I)

Colecciones de datos

Aunque ya sabemos algo sobre diccionarios en Python, lo cierto es que en esta subsección de LENGUAJES hemos hablado mucho de listas (y de tuplas) y muy poco de diccionarios, al menos mucho menos de lo que se merece.

Cuando uno comienza a programar en OOo Basic deberá trabajar con listas o arrays. Si después programa en Python lo "lógico" es que traslade su interés al uso las listas. Además las listas en Python son sencillas e intuitivas de usar y muy útiles.

Pero Python es mucho más rico y complejo que OOo Basic también es esto de las colecciones de datos y los diccionarios son también expresión de esa superioridad: un diccionario mejora la organización de los datos y también la velocidad y precisión del acceso, que está en función del significado y no de la posición como en las listas.

Un diccionario es una colección de elementos organizados mediante pares clavevalor (key:value) en la que cada dato-valor (que sí se puede modificar y duplicar) se almacena asociado a una clave única (por lo que es inmutable y única).



persona = {
    "nombre": "Ana",
    "edad": 34,
    "profesion": "Ingeniera"
}


En este caso el diccionario persona consta de tres claves y cada clave contiene un dato o valor. v.g. la clave "nombre" contiene el valor "Ana"

Si estuviéramos trabajando con listas persona = ["Ana", 34, "Ingeniera"] (1), para obtener la profesión deberíamos expresarlo como persona[2], mientras que como diccionario diremos persona["profesion"]. En el caso de las listas, cualquier modificación del contenido implica un cambio en el procedimiento de acceso que deberemos conocer de antemano; en el caso de los diccionarios es suficiente con conocer el nombre de la clave.

Si lo anterior no fuera suficiente motivo para entender las ventajas de los diccionarios sobre las listas, piensa en la cantidad de "objetos" del mundo real que se pueden expresar como diccionarios:

  • ejemplo: alumno - identificador - nombre - curso - etapa - centro - resultados académicos - MAD - ...
  • Pero también archivo - ruta - tamaño - fecha...

Estos "objetos" y otros muchos se pueden expresar en estructuras clave - valor. Además, Python implementa internamente los diccionarios mediante tablas hash (2), lo que hace que la búsqueda de un elemento por su clave sea, en condiciones normales, extremadamente rápida. En la práctica, acceder a un valor suele realizarse en tiempo constante, independientemente del tamaño del diccionario.

El método más habitual para crear un diccionario consiste en utilizar llaves, como en provincias = {}, instrucción con la que creamos un diccionario vacío que después podemos dotar de contenido, aunque también es posible (y muy frecuente) crear directamente el diccionario definiendo su estructura y contenido, como en este caso...



provincias = {
    "Asturias": "Oviedo",
    "León": "Leon",
    "Cantabria": "Santander"
}


... aunque también se puede crear un diccionario mediante la función dict() - provincias=dict().



# Bien...

provincias = dict(
    Asturias="Oviedo",
    Leon="León",
    Cantabria="Santander"
)

# O también...

provincias = dict([
    ("Asturias", "Oviedo"),
    ("León", "León"),
    ("Cantabria", "Santander")
])


Aunque esta es la forma simple de expresar un diccionario, lo normal es que su contenido sea más complejo. Pensemos, por ejemplo, que creamos el diccionario provincias pero queremos que contenga una colección de datos (capital - población_2025 - peso_sector_primario , peso_sector_secundario, peso_sector_terciario. Ahora ya no hay un ínico dato asociado a la clave, por lo que debemos expresar esta complejidad de otro modo. Y caben dos posibilidades:

  • Como lista provincia:[capital - población_2025 - peso_sector_primario , peso_sector_secundario]
  • o Como diccionarios anidados

Veamos el desarrollo de ambas posibilidades en un script.



# Valor como lista de valores

provincias = {
    "Asturias": ["Oviedo", 1004326, 4.1, 22.7, 73.2],
    "León": ["León", 442000, 8.5, 24.3, 67.2],
    "Cantabria": ["Santander", 593000, 3.8, 20.6, 75.6]
}


# Valor como diccionario anidado lista:valor

provincias = {
    "Asturias": {
        "capital": "Oviedo",
        "poblacion_2025": 1004326,
        "sector_primario": 4.1,
        "sector_secundario": 22.7,
        "sector_terciario": 73.2
    },

    "León": {
        "capital": "León",
        "poblacion_2025": 442000,
        "sector_primario": 8.5,
        "sector_secundario": 24.3,
        "sector_terciario": 67.2
    },

    "Cantabria": {
        "capital": "Santander",
        "poblacion_2025": 593000,
        "sector_primario": 3.8,
        "sector_secundario": 20.6,
        "sector_terciario": 75.6
    }
}


Aunque la primera formulación parece más sencilla de crear, en realidad su manejo resulta más complicado, por lo que es preferible recurrir a los diccionarios anidados por varias razones que no vamos a analizar aquí. Además, los diccionarios anidados constituyen la representación natural en Python de la estructura de un documento JSON. Esta correspondencia casi directa explica que los diccionarios anidados sean la estructura de datos preferida para trabajar con información procedente de diferentes fuentes, espcialmente con las que se expresan como archivos JSON.

Entre otras ventajas, esto hace posible que...



import json

with open("provincias.json", "w", encoding="utf-8") as archivo:
    json.dump(provincias, archivo, ensure_ascii=False, indent=4)


... nos permita almacenar los datos en un documento en formato JSON, el cual podremos visualizar desde bloc de notas.

{ "Asturias": { "capital": "Oviedo", "poblacion_2025": 1004326, "sector_primario": 4.1, "sector_secundario": 22.7, "sector_terciario": 73.2 }, "León": { "capital": "León", "poblacion_2025": 442000, "sector_primario": 8.5, "sector_secundario": 24.3, "sector_terciario": 67.2 }, "Cantabria": { "capital": "Santander", "poblacion_2025": 593000, "sector_primario": 3.8, "sector_secundario": 20.6, "sector_terciario": 75.6 } }

Como puedes comprobar, el archivo JSON reproduce prácticamente la misma estructura jerárquica que nuestro diccionario. Esta correspondencia es precisamente una de las razones por las que los diccionarios tienen un papel tan importante en Python cuando se trabaja con datos estructurados.

Para realizar el proceso inverso (cargar en Python un archivo JSON) deberemos ejecutar este script:



import json

with open("provincias.json", "r", encoding="utf-8") as archivo:
    provincias = json.load(archivo)


Llegados a este punto parece evidente que los diccionarios dan para algo más que una única entrada. Este es un buen momento para reconocerlo y dar por finalizada la actual. Continuará...

NOTAS
1 Esta sería una de las formas en que se podría expresar lo mismo como lista. Pero no es la única. Personalmente, para casos como ese, yo previero usar un procedimiento multilista (una por "campo") y trabajar después con procedimientos de acceso basados precisamente en las posiciones de los elementos de cada una de ellas.
2 Sin entrar en explicaciones que poco nos aportan aquí, una tabla hash es una estructura de datos que utiliza una función hash para transformar una clave arbitraria en un índice numérico que determina dónde se almacenará o buscará el dato asociado dentro de una tabla. Esto hace que se puedan localizar los datos igual de rápido con independencia del tamaño del diccionario (tiempo constante o O(1)), ya que evita la búsqueda secuencial.

jueves, 5 de marzo de 2026

DATOS. Acceso a datos

CSV. Datos estructurados (III)

Acceso y descarga de csv

Vamos a centrarnos en este script en el trabajo con un archivo .csv, pensando no en el acceso (tema ya tratado en esta entrada), sino en tener disponible su contendio desde el script. Eso sí, seguiremos usando el módulo propio CSV, si bien ahora en la opción de diccionario.

Para que se entienda mejor el proceso a seguir, empezaremos por el acceso al archivo .csv. La diferencia principal respecto al script parejo de la entrada anterior es que sustituímos lector = csv.reader(archivo, delimiter=',') por lector_dict = csv.DictReader(archivo). El resto derivan de ésta.



import csv

archivo_nombre = 'datos/datos.csv'										# 1. Definimos la ruta del archivo

try:
    with open(archivo_nombre, mode='r', encoding='utf-8') as archivo:  # 2. Abrimos el archivo en modo lectura ('r')
        lector_dict = csv.DictReader(archivo)                          # 3. Creamos el lector de diccionarios
        print(f"Leyendo datos de: {archivo_nombre}\n" + "-"*30)
        for fila in lector_dict:                                       # 4. Recorremos cada fila (cada fila es un diccionario)
            nombre = fila['Nombre']                                                         
            edad = fila['Edad']        
            print(f"Usuario: {nombre} | Edad: {edad}")                          

except FileNotFoundError:                                              #Control de errores
    print(f"Error: El archivo '{archivo_nombre}' no existe.")
except KeyError as e:
    print(f"Error: No se encontró la columna {e} en el archivo.")


A pesar de las diferencias, ambos archivo comparten la misma limitación: la base de datos sólo está disponible dentro del ámbito de with open(), pero fuera no podemos acceder a los datos, así que si nos interesa deberemos trasladarlos a una colección de datos del propio script, concretamente a una lista de diccionarios.



import csv

archivo_nombre = 'datos/datos.csv'                                                  # 1. Definimos la ruta del archivo

datos_internos = []                                                                 # Lista para almacenar la base de datos en memoria


try:
    with open(archivo_nombre, mode='r', encoding='utf-8') as archivo:  				# 2. Abrimos el archivo en modo lectura ('r')
        lector_dict = csv.DictReader(archivo)                                       # 3. Creamos el lector de diccionarios
        print(f"Leyendo datos de: {archivo_nombre}\n" + "-"*30)
        for fila in lector_dict:                                                    # 4. Recorremos cada fila (que es un diccionario)
            nombre = fila['Nombre']                                                         
            edad = int(fila['Edad'])
            datos_internos.append(fila)        
            print(f"Usuario: {nombre} | Edad: {edad}")                          
        print(f"--- Carga finalizada ---")
        print(f"Se han almacenado {len(datos_internos)} registros en el diccionario interno.")

    if datos_internos:																# Ejemplo: Acceder al primer registro almacenado
        primero = datos_internos[0]
        print(f"Primer registro en memoria: {primero['Nombre']} tiene {primero['Edad']} años.")
        
except FileNotFoundError:                                                           #Control de errores
    print(f"Error: El archivo '{archivo_nombre}' no existe.")
except KeyError as e:
    print(f"Error: No se encontró la columna {e} en el archivo.")

#Ejemplo de acceso a la estructura del diccionario ---------------------------------------------------------------------------------------

registro_ejemplo = datos_internos[0]                   # Tomamos el primer diccionario de nuestra lista para el ejemplo
print(f"Claves: {list(registro_ejemplo.keys())}")      # A. Identificar solo las CLAVES
print(f"Valores: {list(registro_ejemplo.values())}")   # B. Identificar solo los VALORES


Lo que nos permite importar al script el contenido del archivo .csv empieza aquí (datos_internos = []) y se concreta en estas dos líneas:
  • for fila in lector_dict: ----> Recorre el archivo .csv
  • ----datos_internos.append(fila) ----> Añade contenido a la lista (de diccionarios) datos_internos

Ellas hacen posible el correcto acceso a los datos mediante instrucciones como print(f"Claves: {list(registro_ejemplo.keys())}"), que muestra las claves del diccionario.

Cierto que la consecuencia es un incremento de la carga de memoria, pero la ventaja en rapidez de funcionamiento compensa cuando necesitamos realizar diferentes acciones sobre los datos desde un mismo script. Tal es el caso del que sigue y con el que finalizamos esta entrada: un script mediante el que accedemos a una base de datos, la cargamos en memoria y realizamso sobre ella un conjunto de acciones que aquí se concretan en seleccionar y filtrar registros.



import csv

# --- FASE 1: CARGA DE DATOS  ---------------------------------------------------

ruta_archivo = 'datos/datos.csv'
datos_internos = []

try:
    with open(ruta_archivo, mode='r', encoding='utf-8') as archivo:
        lector = csv.DictReader(archivo)
        for fila in lector:
            fila['Edad'] = int(fila['Edad']) # Convertimos el valor de [Edad] a entero para poder filtrar
            datos_internos.append(fila)
    print(f"Sistema listo. {len(datos_internos)} registros cargados.\n")

except FileNotFoundError:
    print("Error: No se encontró el archivo en D:/. Por favor, créalo primero.")
    exit() # Finaliza el script si no hay datos

# --- FASE 2: OPCIONES ------------------------------------------------------------

while True:
    print("\n===============================")
    print("   GESTOR DE CONSULTAS")
    print("===============================")
    print("1. Buscar por NOMBRE (Coincidencia parcial)")
    print("2. Filtrar por EDAD (Exacta o Rango)")
    print("3. Salir")
    
    opcion = input("\nSeleccione una opción: ").strip()

    if opcion == '3':
        print("Saliendo del sistema...")
        break

# --- OPCIÓN 1: BÚSQUEDA POR NOMBRE ---
    if opcion == '1':
        termino = input("Escriba el nombre o parte de él: ").strip().lower()
        encontrados = [r for r in datos_internos if termino in r['Nombre'].lower()]
        
        print(f"\nResultados para '{termino}':")
        if encontrados:
            for e in encontrados:
                print(f"ID: {e['Nombre']} | Edad: {e['Edad']}")
        else:
            print("No se encontraron coincidencias.")

# --- OPCIÓN 2: FILTRO POR EDAD ---
    elif opcion == '2':
        print("\n--- Filtro de Edad ---")
        print("A. Edad exacta")
        print("B. Rango (Mínimo y Máximo)")
        sub = input("Elija modalidad (A/B): ").strip().upper()

        resultados = []
        try:
            if sub == 'A':
                objetivo = int(input("Edad a buscar: "))
                resultados = [r for r in datos_internos if r['Edad'] == objetivo]  # Identificamos si el valor de la clave 'Edad' coincide
            
            elif sub == 'B':
                v_min = int(input("Edad mínima: "))
                v_max = int(input("Edad máxima: "))
                resultados = [r for r in datos_internos if v_min <= r['Edad'] <= v_max]  # Filtro por intervalo de valores          
         
            if resultados:
                print(f"\nSe hallaron {len(resultados)} personas:")
                for r in resultados:
                    print(f"• {r['Nombre']} - {r['Edad']} años")
            else:
                print("No hay registros en ese rango.")
                
        except ValueError:
            print("Error: Por favor, ingrese solo números enteros para la edad.")

    else:
        print("Opción no válida. Por favor, marque 1, 2 o 3.")

print("\nPrograma finalizado correctamente.")


NOTA: El archivo .csv de prueba es necesario para el correcto funcionamiento de estos tres script, aunque siempre los puedes adaptar para que funcionen con un .csv diferente. No obstante, si prefieren no hacerlo, aquí lo puedes descargar.

miércoles, 11 de febrero de 2026

LENGUAJES. Python

Usos prácticos de listas (I)

Colecciones de datos

Sabemos qué es una lista en Python y cómo podemos usarla, pero nos interesa conocer xómo y para qué podemos usarlas en la práctica. Una de estas utilidades será el tema de esta entrada, concretamente su uso como soporte para la recopilación de datos.

Dado que las listas permiten incluir diferentes tipos de datos, que los datos estén repetidos y que sea posoble modificarlos, estas colecciones son muy útiles para situaciones en las que tenemos que recopilar datos de distintos tipos y además desconocemos cuantos.

Un ejemplo de ello es cuando introducimos por teclado los resultados de la aplicación de una prueba grupal. En este caso tenemos datos de distinto tipo (string para nombres, datos en formato fecha, valores numéricos que se pueden repetir...) y no sabemos a priori de cuantos datos estamos hablando, ya que unos alumnos pueden haber resulto n ejercicios y otros n+/x.

En estas condiciones, podemos empezar declarando una lista vacía y después darle contenido desde teclado mediante un bucle. Una vez cumplimentado el "registro" procedemos a almacenarlo en un archivo a modo de base de datos.

Vamos a pedirle a Gemini que genere un script que cumpla estos objetivos y a analizar el modo en que plantea el uso de la lista.



import os

def registrar_evaluacion_continua():
    nombre_archivo = "base_datos_alumnos.txt"

    # Si el archivo no existe, creamos la cabecera para identificar columnas
    if not os.path.exists(nombre_archivo):
        with open(nombre_archivo, "w", encoding="utf-8") as f:
            headers = ["Nombre", "Apellidos", "Edad", "Curso"]
            items_headers = [f"Item_{i:02d}" for i in range(1, 31)]
            f.write(",".join(headers + items_headers + ["PD"]) + "\n")

    while True:
        print("\n" + "="*40)
        # 1. Recogida de identificación
        nombre = input("Nombre: ")
        apellidos = input("Apellidos: ")
        edad = input("Edad: ")
        curso = input("Curso (1º a 6º EP): ")

        # Iniciamos lista: [Nombre, Apellidos, Edad, Curso]
        sujeto_datos = [nombre, apellidos, edad, curso]

        aciertos = 0
        finalizado_por_tiempo = False

        print(f"\nIntroduzca resultados para {nombre} (1:Acierto, 0:Fallo, Enter:Fin tiempo)")

        # 2. Bucle de 30 ítems
        for i in range(1, 31):
            if not finalizado_por_tiempo:
                res = input(f"Ítem {i:02d}: ").strip()
                if res == "":
                    finalizado_por_tiempo = True
                    sujeto_datos.append("") # Celda vacía para el resto
                else:
                    sujeto_datos.append(res)
                    if res == "1":
                        aciertos += 1
            else:
                sujeto_datos.append("") # Relleno por no finalizado

        # 3. Añadir Puntuación Directa (PD) al final
        sujeto_datos.append(str(aciertos))

        # Feedback inmediato al usuario
        print(f"\n>>> REGISTRO FINALIZADO: {nombre} {apellidos}")
        print(f">>> Puntuación Directa obtenida: {aciertos}")

        # 4. Guardado persistente (Modo 'a' para no borrar sesiones previas)
        with open(nombre_archivo, "a", encoding="utf-8") as f:
            f.write(",".join(sujeto_datos) + "\n")

        # Opción de cerrar el script tras este alumno
        if input("\n¿Desea registrar a otro alumno ahora? (s/n): ").lower() != 's':
            break

    print(f"\n[SISTEMA] Datos sincronizados en '{nombre_archivo}'.")

if __name__ == "__main__":
    registrar_evaluacion_continua()


Este script funciona correctamente en local (trasladándolo al IDE) y se ajusta a lo solicitado al bot, aunque fue necesario ir puliendo la demanda hasta conseguirlo.

El problema, no obstante, es que no nos ayuda a entender y a aprender. Resuelve la tarea según unos parámetros que no quedan explicitados y que son corregidos cuando se le requiere, aunque tampoco se explica porqué no hizo antes lo que ahora es necesario, pero reincide en una propuesta que se aleja del modo "didáctico" en que queremos que lo haga para, ajustándose al nuestro actual nivel de complejidad en la demanda, facilite nuestro proceso de aprendizaje: su meta es fundamentalmente la eficiencia, resolver el problema.

Para lograr esto es necesario:

  • Subdivir la tarea paso a paso.
  • Pedir al chatbot que ejecute esa construcción secuencial del script.
  • Realizar sobre esas propuestas las modificaciones que consideremos pertinente.

De este modo es posible alcanzar nuestro objetivo, que es comprender el script antes que conseguir que funcione.Y todo ello en relación con nuestro objetivo inicial: el uso práctico de una lista como recurso para recopilar datos.

Desarrollaremos este procedimiento en entradas próximas. Dada su complejidad y el tiempo que requiere para su desarrollo, por el momento será suficiente con presentarlo.

martes, 3 de febrero de 2026

LENGUAJES. Python.

Listas. Manejo básico

Colecciones de datos

Las listas son las colecciones nativas de datos más simples y flexibles con las que cuenta Python lo que hace que sean usadas con mucha frecuencia.

Como dije en su presentación, una lista es una colección ordenada y mutables de datos. Además los datos pueden ser de diferentes tipos (caracteres, cadenas, enteros, reales, booleanos) y pueden estar repetidos. Esto, junto con las operaciones permitidas sobre ella, la convierten en un recurso muy flexible.

En este cuaderno te explico cómo trabajar con listas.

lunes, 2 de febrero de 2026

LENGUAJES. Python

Colecciones nativas

Colecciones de datos

Además de las variables, Python también permite trabajar con colecciones de datos. En el lenguaje-base están disponibles cuatro colecciones de datos, pero mediante bibliotecas complementarias podemos ampliar esta tipología básica. De momento, en esta entrada, nos limitaremos a presentar las colecciones nativas.

Empecemos por un breve resumen que nos ofrece Gemini a modo de síntesis.

Colecciones Ordenada Mutable Datos duplicados
Lista
Tupla No
Conjunto (Set) No No
Diccionario Claves - N
Valores - Sí

En esta entrada, que hace de presentación, vamos a explicar brevemente cada una de ellas. Desde este enlace puedes acceder a la información que ofrece la página oficial de Python y en entradas posteriores trataremos cada una de ellas de forma más detallada y por separado.

Listas (list). Son colecciones ordenadas y mutables que permiten incluir datos duplicados. Constituyen la mejor opción cuando necesitas trabajar con una secuencia de elementos ordenados y que se van a modificar frecuentemente. A nivel de sintaxis se caracterizan por estar delimitadas por corchetes mi_lista = [1, "hola", 3.14]

Tuplas (tuple) Son colecciones ordenadas e inmutables, lo que implica que no se pueden cambiar una vez creadas. Esto hace que sean rápidas en el manejo y más seguras para datos que no deben variar. A nivel de sintáxis se presentan delimitadas por paréntesis mi_tupla = (10, 20, 30)

Conjuntos (set) Son colecciones desordenadas y no indexadas. Su característica principal es que no permiten duplicados. Sí admiten el cambio de datos, pero sólo en el sentido de que es posible añadir o quitar elementos siempre que estos, a su vez, sean inmutable. Esto implica que podemos añadir números, cadenas o tuplas (que son inmutables), pero no listas (que son mutables). Son muy eficientes para operaciones matemáticas como uniones o intersecciones de conjuntos. Si convertimos una lista en un conjunto se eliminarán los elementos repetidos que podía contener la lista, lo que permite plantear procedimientos interesantes. Uno de ellos es la verificación de la pertenencia de un dato a un conjunto. Su sintaxis mi_set = {1, 2, 3, 3} es el uso de las llaves como delimitador.

Diccionarios (dict) Son colecciones de tipo par [clave-valor], extremadamente rápidos para buscar información conociendo la clave. Su uso es interesante para representar objetos (POO) y para trabajar con bases de datos en memoria o en mapeos. Se identifican sintácticamente por estar delimitados por llaves mi_dict = {"nombre": "Ana", "edad": 25} junto con la estructura clave-valor

viernes, 16 de mayo de 2025

LENGUAJES . Python

Listas y tuplas. Operadores

Colecciones de datos

Los operadores son, como ya sabemos, componentes de todo lenguaje de programación que permiten manipular los datos referenciados en las variables de diferentes maneras, según el tipo de dato y de operador de que se trate. También las colecciones de datos admiten la acción de operadores. Las listas y las tuplas comparten los mismos.

Los operadores disponibles en listas y tuplas son los siguientes:

  • : Permite obtener segmentos del contenido de ambas colecciones.
  • + y * Permiten realizar las operaciones sobre listas y tuplas. Veremos cuáles.
  • in / not in Permiten verificar la presencia / no presencia de determinado dato dentro del conjunto.
  • Los operadores relacionales <, <=, >, >= y !=, cuyas funciones describiremos en su momento.

Aunque figure en primer lugar, no trataré el operador : en esta entrada, dada la relación que mantiene con el acceso a los datos. Por este motivo desplazo su análisis a la entrada correspondiente.

Inicio este análisis con los operadores + y *, los cuales cumplen funciones específicas con listas y tuplas, diferentes de las que permiten ejecutar en variables.

  • El operador + permite la unión o concatenación de los elementos de varias listas. Como cabe suponer por lo que sabemos de los operaciones aritméticos, nos permiten realizar operaciones de suma y multiplicación con listas y tuplas.

lista1 = list([1,2,3,4])
lista2 = list([5,6,7,8])
print(lista1 + lista2) 

-> devuelve [1,2,3,4,5,6,7,8]

  • Este mismo operador, aplicado a tuplas funciona exactamente igual, aunque lógicamente devuelve una tupla (1,2,3,4,5,6,7,8)
  • La operación * tiene como efecto la repetición de la lista o la tupla sujeta a tal operación, siendo indiferente que el operador se sitúe antes o después de la lista o tupla. Así que print(3 * lista1) devuelve [1,2,3,4,1,2,3,4,1,2,3,4] y print(tupla1 * 2) devuelve (1,2,3,4,1,2,3,4). En ambos casos el número debe ser entero (ni 3.1 ni 2.4 se admiten como "multiplicadores")
  • Ambos operadores tienen el mismo efecto con independencia de la naturaleza de los elementos de la lista o de la tupla.
  • En ambos casos no está permitido el trabajo con listas y tuplas simultáneamente. No es posible ni lista + tupla, ni tupla * lista, ni ninguna otra combinación o permutación de elementos.
  • Los operadores in / not in permiten identificar si un dato forma parte o no de una lista o de una tupla. Ambos devuelven True o False según el resultado de la operación. Ejemplos para lista1 y tupla1 anteriores:


print(5 in lista1) #devuelve False
print(1 in lista1) #devuelve False

print(5 not in lista1) #devuelve True
print(1 not in lista1) #devuelve True

print(5 in tupla1) #devuelve False
print(1 in tupla1) #devuelve False

print(5 not in tupla1) #devuelve True
print(1 not in tupla1) #devuelve True


Los operadores relacionales permiten comparar dos listas entre sí, elemento a elemento y según criterios específicos en función del tipo de elemento. También devuelven True o False, según el resultado de la comparación. Veamos algunas comparaciones, tomando como referencia las listas y las tuplas creadas antes. Dada la multiplicidad de opciones me limitaré a unos pocos ejemplos, aunque los resultados se pueden hacer extensibles al resto de los operadores:



#Operadores relacionales con listas

print(lista1 == lista1) #Devuelve True

print(lista1 != lista2) #Devuelve True

#Operadores relacionales con tuplas

print(tupla1 == tupla1) #Devuelve True

print(tupla1 != tupla2) #Devuelve True

#Operadores relacionales con listas y tuplas

print('Operadores relacionales con listas y tuplas')

print(lista1 == tupla1) #Devuelve False por ser una lista y la otra tupla, aunque el contenido es el mismo


jueves, 15 de mayo de 2025

LENGUAJES. Python

Listas y tuplas. Automatizar contenido

Colecciones de datos

Aunque la forma más frecuente de crear colecciones de datos es introducirlos directamente mediante teclado o acceder a documentos en los que estos se encuentren disponibles, en ocasiones es necesario crear colecciones de datos numéricos más o menos amplias cuyos elementos mantienen entre sí una relación, lo que hace que sea posible automatizar su creación. Para estos casos es muy útil conocer procedimientos de creación automática de colecciones de datos. Y existen, tanto para listas y como para tuplas.

El primer procedimiento para crear listas (y tuplas) automáticamente es el que utiliza la función range(). Esta función precisa tres parámetros: el valor de inicio, el valor final y el incremento. Un ejemplo: range(2,12,2 nos permite crear una colección de datos que inicia en el número 2 y finaliza en el 10 (el 12 no está incluido), incrementándose el valor inicial de 2 en 2 (2 - 4 - 6...).

Utilizamos el constructor de listas y creamos automáticamente una que cumple esos criterios. Si la lista es pequeña podemos hacerlo manualmente, pero si contiene muchos elementos es más funcional usar la función range() que acabo de explicar:

  • lista_1 = list(range(2,100,2)) crea una lista con todos los números pares comprendidos entre 2 y 100 (100 no incluido).
  • tupla_1 = tuple(range(2,100,2)) tiene el mismo efecto práctico, aunque en este caso se trata de una tupla, con todo lo que esto implica respecto a inmutabilidad.

Existen otros procedimientos que además de permitirnos crear colecciones de datos numéricos más complejas que también es necesario conocer si nos encontramos con script que utilicen estas expresiones. Me estoy refiriendo a la creación de listas o tuplas mediante operadores, bucles y condicionales. Veamos algunos ejemplos:

  • lista_3 = [x * 2 for x in lista_1] es una lista de valores numéricos que utiliza lista_1 (recuerda: números pares comprendidos entre 2 y 98) multiplicando por 2 cada uno de ellos. Mediante for x in lista_1 recorre esa lista.
  • La tupla_2 =tuple([x * 2 for x in tupla_1]) hace lo mismos pero creando una tupla y usando una tupla como base. Observa que ahora necesitamos usar el constructor y una combinación de ([])
  • La lista_4 = [x**2 for x in lista_1 if x > 8] hace los mismo que lista_2, pero ahora añadimos como condición para la creación de la colección de datos que el valor original de lista_1 sea superior a 8 if x > 8)
  • Finalmente, en lista_6 = [x * 3 for x in range(2,32,2) if x >10] utilizamos directamente la función range() para crear automáticamente la colección de datos de base for x in range(2,32,2) y mantenemos una condición: que los valores resultantes sean superiores a 10 if x >10

Digo finalmente para no prolongar esta casuística más de lo necesario, porque aun caben otras combinaciones, incluyendo la de listas y tuplas y el uso de doble bucle en función de una doble variable lista_5 = [x + y for x in lista_1 for y in lista_2]

Documento. En este enlace te dejo acceso a un script con ejemplos, incluidos los explicados en esta entrada.

martes, 13 de mayo de 2025

LENGUAJES. Python

Acceso a listas y a tuplas

Colecciones de datos

Aunque listas y tuplas son colecciones de datos diferentes, ya dijimos que comparten muchas características y comportamientos; entre estos últimos están su posible contenido y el modo en que podemos acceder a él.

El acceso a los elementos de las listas y las tuplas es un procedimiento de uso muy frecuente y de mucha utilidad práctica, por lo que interesa conocer cómo realizarlo. Ya sabemos cómo es el procedimiento básico por haberlo visto al hablar de ambas colecciones de datos (v.g. print(mi_tupla_a[0] nos devuelve en pantalla el primer elemento de la tupla), pero esto nos sirve cuando la tupla o la lista contienen elementos simples (vg mi_lista_a = [12,23,43,56]), pero, ¿ cómo accedemos a los elementos complejos, a las listas o tuplas anidadas dentro de la lista o tupla principal?.

Un ejemplo de ello podría ser el siguiente: una lista que contiene "registros" que se contienen a su vez una lista (1):

  • mi_lista_b = [['Lucía','Martínez Alonso','P4'],['Mario','Carcedo Suárez','P3'],['Carlota','Álvarez López','P2']]

Veamos mediante código distintas posibilidades de acceso al contenido de esta lista. Denominaré registro a cada una de las tres sublistas y dato a cada uno de los elementos de cada uno de los registros (dato -> nombre, dato -> apellidos, dato -> curso)



#Acceso a la lista completa

print(mi_lista_b)

#Captura del primer registro

reg1 = mi_lista_b[0]

#Acceso al primer registro de la lista

print(mi_lista_b[0])

#Acceso al primer registro que pasamos previamente a reg1

print(reg1)

#Acceso al primer dato (dato -> nombre) de cada uno de los registros de la lista

reg_nombres = [[mi_lista_b[0][0]], [mi_lista_b[1][0]],[mi_lista_b[2][0]]]

print(reg_nombres)


#Acceso al primer dato del nueva lista reg_nombres

print(reg_nombres[0])

#Otro modo de acceso (ahora reg_nombres_b es una lista de elementos imples

reg_nombres_b = [mi_lista_b[0][0], mi_lista_b[1][0],mi_lista_b[2][0]]

print(reg_nombres_b)


En este script estamos jugando con diferentes formas de acceder al contenidos de una lista compleja (lista de listas). Podemos diferenciar el modo de acceder al conjunto de la lista print(mi_lista_b) y a su primer elemento print(mi_lista_b[0]). En el primer caso nos devuelve todos los registros de la lista (la lista con sus sublistas...

  • [['Lucía', 'Martínez Alonso', 'P4'], ['Mario', 'Carcedo Suárez', 'P3'], ['Carlota', 'Álvarez López', 'P2']]

... y en el segundo sólo el primer registro (la primera sublista)

  • ['Lucía', 'Martínez Alonso', 'P4']

Si lo que queremos es acceder al primer dato de la primera sublista, deberemos expresarlo como sigue:

  • [mi_lista_b[0][0]]) - nos devuelve Lucia

Observa que empleamos una pareja de corchetes para identificar el primer registro de la lista principal (que aquí ni se nombra) reg_nombres_b y que se concreta como mi_lista_b[] y a continuación una segunda pareja de corchetes para identificar el dato que deseamos (también el primero [0][0])

Haciendo extensible este procedimiento, podemos crear una lista de listas que contienen como único registro el nombre de cada alumno...

  • reg_nombres = [[mi_lista_b[0][0]], [mi_lista_b[1][0]],[mi_lista_b[2][0]]]

... o, como alternativa, una lista de elementos simples con el mismo contenido

  • reg_nombres_b = [mi_lista_b[0][0], mi_lista_b[1][0],mi_lista_b[2][0]]

La diferencia es sutil pero fundamental: en el primer caso delimitamos doblemente con corchetes cada elemento, en el segundo no. Con el primer procedimiento obtenemos una lista de listas simples (de un sólo elemento o dato cada una)...

  • [['Lucía'], ['Mario'], ['Carlota']]

... y con el segundo una lista simple de tres elementos (2)

  • ['Lucía', 'Mario', 'Carlota']

Además de acceder a elementos concretos de la lista o de la tupla, también podemos hacerlo a un subconjunto de datos mediante el operador :. De este operador ya hablamos en esta entrada, pero dejamos aplazado su tratamiento para relacionarlo con los procedimientos de acceso al contenido, motivo por el que corresponde ahora tratarlo.

Este operador presenta una formulación básica de uso lista/tupla[inicio:fin] en la que se indica la posibilidad de delimitar el inicio del subconjunto y su final (mejor dicho, el elemento previo a su final). Por ejemplo, para lista0 = [1,2,3,4,5,6,7,8,9], print(lista0[0:3]) nos devuelve [1,2,3], dado que se cuentan los elementos empezando por 0 y el elemento posición 3 es 4, por lo que el anterior (elemento 2 -> 3).

Ahora podemos omitir tanto el elemento inicio como el elemento fin. En el primer caso (vg. lista0[:3]) el resultado será el mismo que antes ([1,2,3]) ya que la ausencia del delimitador inicio implica partir del primer elemento de la lista (o de la tupla). Si omitimos el elemento final (vg lista0[2:]) implícitamente se supone que deseamos ir desde el elemento inicio (posición 2 -> 3) hasta el final de la lista. En consecuencia, para lista0 -> [3,4,5,6,7,8,9].

Otra forma de acceder a segmentos del contenido de listas o tuplas es empleando índices negativos...



print(lista0[-5:-3]) 

#-> devuelve [5,6]

print(tupla0[-2:]) 

#-> Para tupla0, igual resultado que para lista0: devuelve (8,9)


... debido a que los valores negativos remiten al orden inverso en el acceso al contenido de listas y tuplas.

NOTAS
1 El mismo ejemplo podríamos plantear mediante una tupla de tuplas, una lista de tuplas o una tupla de listas.
2 Podríamos hacer exactamente lo mismo con una tupla y obtendríamos los mismos resultados

lunes, 28 de octubre de 2024

Lenguajes. Python.

Diccionarios


El último tipo de colección de datos en Python es el diccionario, estructura cuyos elementos o componentes son pares llave/valor que denominamos entrada.


La llave puede ser cualquier elemento hashable, esto es, un objeto cuyo valor no cambia a lo largo de toda la ejecución del programa, siendo un hash el valor que identifica dichos objetos. Además, la llave no puede estar duplicada. El valor puede ser de cualquier tipo y puede estar repetido. Esta estructura del diccionario nos permite un rápido acceso a los datos que contiene.

La sintaxis del diccionario es la siguiente: al igual que los conjuntos, {} para delimitar su inicio y fin, entradas (pares llave/valor) separadas por comas y elemento llave-valor separados por dos puntos. 

  • Podemos declarar un diccionario directamente:

alumnos={"1º":"Juan", "2º":"Luis", "3º":"Melisa"}

  • O bien mediante el constructor de clase dict

alumnos = dict() -> Diccionario vacío
alumnos= dict(Uno="Juan", Dos="Luis", Tercero="Melisa") -> los números no son identificadores válidos, por lo que no pueden usarse como claves. Por este motivo, esta fórmula es poco empleada.

Al contario que los conjuntos, sí es posible acceder a un elemento concreto de un diccionario. Para ello utilizaremos la referencia llave, de forma similar a cómo hacíamos con listas y tuplas. Por ejemplo, para acceder al elemento 2, solicitaremos...

print(alumnos["2º"]) -> Devuelve Luis.

En este caso, tal y como definimos el contenido del diccionario, no podemos solicitar alumnos[1], ya que 1 no es la llave, sino 2º. Ahí radica una de  las diferencias entre diccionarios y listas/tuplas. Incluso aunque nuestro diccionario se configure como alumnos={1:"Juan", 2:"Luis", 3:"Melisa"}, si solicitamos el alumno situado como primera entrada deberemos hacerlo utilizando la llave de esa entrada (print(alumnos[1])), no su índice (print(alumnos[0])), orden que nos dará error.

También es posible añadir un nuevo elemento a un diccionario existente mediante el uso de la siguiente expresión: NombreDiccionario[llave]=valor. Por ejemplo, si deseamos añadir un cuarto elemento a nuestro diccionario alumnos diremos...

alumnos["4º"]="Carmela"

... por lo que si solicitamos ver el contenido de alumnos (print(alumnos)) obtendremos {1: 'Juan', 2: 'Luis', 3: 'Melisa', '4º': 'Carmela'}.

Igual que en lo relativo a las variables y al resto de las colecciones de datos, aun son muchas las cosas por aprender respecto a los diccionarios, y las iremos viendo en entradas sucesivas, pero por el momento considero que es suficiente como presentación y primer acercamiento a los diccionarios y, por extensión, a las colecciones de datos, por lo que cierro aquí, provisionalmente, este conjunto de entradas dirigidas a su presentación y primer análisis.

jueves, 13 de junio de 2024

Lenguajes. Python.

Colecciones de datos. Conjuntos

El tercer tipo de colección de datos disponible en Python es el conjunto.

Un conjunto se diferencia de lista y tupla en dos características: no dispone de índice, por lo que no se puede acceder a su contenido mediante referencia a índice o valor de posición, y no admite elementos repetidos, lo que le asemeja al concepto matemático de conjunto.


Se recomienda utilizar conjunto en lugar de listas o tuplas cuando el volumen de datos que contiene la colección es muy elevada, ya que los conjuntos son mucho más eficientes: el acceso a la colección es en Python mucho más rápida en conjuntos que en listas o tuplas.

La sintaxis básica de un conjunto es su delimitación mediante llaves ({}), lo que permite crearlos directamente, asociados a una variable, mediante la siguiente instrucción (como ejemplo):

a = {1,3.4,"Casa"}

Al pedir la devolución de un conjunto tenemos que saber que el orden de devolución no es necesariamente el orden en que introdujimos los datos, ya que Python procede a ordenarlos según criterios propios. Por ejemplo, el conjunto {1,7,21,2} es devuelto como {1,2,21,7}

Dado que los conjuntos carecen de índice, no es posible acceder a un elemento específico de los que componen un conjunto, pero sí es posible transformar conjuntos en listas o tuplas, como también lo es convertir ambas en conjuntos. Para ello emplearemos los denominados "constructores de clase": list(), tuple() y set(). Este constructor también es necesario para crear un conjunto vacío, ya que no podemos hacerlo directamente:
  • Conjunto vacío: c0=set() -> devuelve set()
  • Conjunto a partir de lista: cl=set([1,2,3]) -> devuelve set{1,2,3}
  • Lista a partir de conjunto: lc = list({1,2,3} -> devuelve [1,2,3]
  • Conjunto a partir de tupla: ct=set((2,5,7)) -> devuelve {2,5,7}
  • Tupla a partir de conjunto: tc=tuple({3,4,5}) -> devuelve (3,4,5)
Si queremos obtener el contenido individual de una determinada posición en un conjunto, primero deberemos obtener el orden que ocupa ese elemento en el conjunto, ya que no es igual al orden en que lo introducimos, y después convertirlo en lista o tupla y solicitar la posición que nos interese. Por ejemplo:

cj={24,"pato","9",2.3}  -> Creamos el conjunto
print(cj) -> Comprobamos el orden {24, '9', 2.3, 'pato'}
ls=list(cj) -> Convertimos el conjunto en una lista
print(ls[0]) -> Solicitamos el dato situado en primer lugar 24

NOTA. En estos casos siempre es preferible trabajar con listas o tuplas, pero podría ser conveniente utilizar conjuntos si el volumen de datos es muy elevado.

LENGUAJES. Python.

Tuplas. Manejo básico

Colecciones de datos

Después de las listas, las tuplas son el segundo tipo de colecciones de datos del lenguaje Python. En esta entrada, siguiendo el modelo de la anterior, recordaremos sus características y aprenderemos a manejarlas.

La palabra tupla no existe en español, aunque sí en el lenguaje matemático, derivada de la generalización de la secuencia dupla, tripla.. n-tupla..., esto es, como lista ordenada finita de elementos.

En informática, una tupla es una lista inmutable y ese es el significado que tiene también en Python. En consecuencia, una tupla no es más que una lista cerrada, que no se puede modificar una vez creada, lo que equivale a decir que es un tipo específico de lista.

Mientras que una lista se pueden modificar (por lo que podríamos redefinirla como listas abierta o mutable), una tupla (o lista cerrada o inmutable) no se pueden modificar.

En Python para diferenciar lista de tupla se modifica su sintaxis básica: mientras la lista se identifica por el uso de corchetes, en la tupla se emplea el paréntesis...

  • miLista = ["perro","gato","lobo"]
  • miTupla = ("perro","gato","lobo")

... pero en ambas los datos se separan por comas y es el valor de posición (el índice) que ocupan en su seno lo que permite identificarlos (1):

  • print(miLista[1]) - gato
  • print(miTupla[1]) - gato

Al ser las tuplas inmutables las podemos considerar equivalentes a las constantes frente a las variables (que serían las listas), por lo que su utilidad es equivalente a la de aquellas. Los tuplas no admiten ciertas operaciones que sí son posibles en las listas, como añadir elementos, modificarlos o borrarlos, por lo que no cambian ni de estructura ni de contenidos a lo largo de todo el script.

No obstante, una tupla no deja de ser una colección de datos que puede contener distintos tipos de elementos. Del mismo modo que las listas pueden contener, además de datos de diferentes tipos, otras listas y también tuplas, las tuplas pueden tener la misma composición, incluyendo listas. Y al ser las listas elementos mutables, una tupla que contenga una lista podrá ser modificable, no en cuanto tupla, pero sí ese elemento de la tupla definido como lista.

Detengámonos un momento a analizar estas cuestiones.



miTupla = ("casa","mesa")
print(miTupla) -> ('casa', 'mesa')
miTupla[0]="silla"

#-> Devuelve error 

Traceback (most recent call last):
  File "", line 1, in 
    miTupla[0]="silla"
TypeError: 'tuple' object does not support item assignment


Aquí hemos definido una tupla de dos elementos. Si queremos modificar uno de ellos mediante asignación (como hacemos en una lista) el resultado es un mensaje de error que nos informa que el objeto tupla no soporta la asignación de ítem, o sea, que no se puede modificar. Tampoco podríamos hacer otros cambios que sí son posibles en las listas, como añadir un nuevo elemento o borrarlo.



miLista=[1,2,3]
miTupla=("casa","mesa",miLista)

print(miTupla) -> ('casa', 'mesa', [1, 2, 3])

miTupla[0]="armario" 

# -> Devuelve error
Traceback (most recent call last):File "", line 1, in  miTupla[0]="armario". TypeError: 'tuple' object does not support item assignment 

print(miTupla[2][0]) 

#-> 1
miTupla[2][0]=0
print(miTupla) -> ('casa', 'mesa', [0, 2, 3])


En este segundo ejemplo, la tupla contiene tres elementos, siendo el tercero [2], una lista definida con anterioridad miLista. Podemos comprobar de nuevo que si queremos modificar el elemento [0] nos devuelve error, pero si lo que deseamos es modificar el elemento [2][0] (tercer elemento de la tupla y primero de esa lista), es perfectamente posible hacerlo: la orden miTupla[2][0]=0 no devuelve error y se ejecuta correctamente.

  • print(miTupla) ->('casa', 'mesa', [0, 2, 3])

Para finalizar esta entrada, advirtiendo antes que aun quedan muchas cosas por aprender de las listas y de las tuplas, vamos a ver una aplicación práctica combinando listas y tuplas y bucles o ciclos.

Este ejercicio se puede considerar complementario tanto de esta entrada como de la anterior, ya que desarrolla un tema que en aquella quedó indicado pero no resuelto. Dije entonces que podíamos crear listas sin elementos (sin contenido), pero no dije qué utilidad podrían tener tales listas. Ahora veremos una de ellas. Utilizar una tupla como marco de referencia, concreta también una de sus utilidades (2): respetar la estructura básica de una formulación asimilable a base de datos.

El código resultante queda aquí a tu disposición para que lo estudies. La idea es que crees tú algún otro parecido y compares tu solución con la mía, que no es perfecta, puesto que el resultado final (Ejemplo para dos alumnos hipotéticos)...

  • ['Carmela', 'Alejandro'], ['Álvarez López', 'Ramírez Suárez'], ['1', '1', '1', '0', '1', '0', '1', '1', '0', '0', '1', '1', '1', '0']

... no es del todo satisfactorio: mi idea era obtener el siguientes resultado para el tercer componente de la tupla (dos elementos lista en lugar de un único elemento)

  • [['1', '1', '1', '0', '1', '0', '1'],[ '1', '0', '0', '1', '1', '1', '0']]

... pero no me ha sido posible... por el momento.

Aunque este error relativo respecto al objetivo es importante y habrá que estudiar cómo remediarlo, sí hay algunos logros de interés que te quiero comentar brevemente:

  • En la entrada sobre listas decía que la expresión lstPalabras = lstPalabras + ["mesita"] permitía añadir un nuevo elemento a la lista, y es correcto, pero para añadir un elemento en otros contextos de mayor complejidad necesitamos emplear una función append() como puedes observar en el ejemplo:
    • vNombre=input("Nombre: ")
    • lstNombres.append(vNombre)
  • Podemos declarar listas vacías y cumplimentarlas mediante un bucle en función de una decisión previa: en mi ejemplo, el número de alumnos del listado, que sirve para establecer el número de ciclos o iteraciones que realiza el bucle for
    • vNumAl = int(input("Dime el número de alumnos y alumnas del grupo: "))
    • (...)
    • for i in range(vNumAl):
  • Podemos anidar un bucle for dentro de otro bucle, generando estructuras complejas. En mi ejemplo el bucle principal contiene un bucle secundario. Las iteraciones del primero vienen dadas interactivamente, pero las del segundo vienen determinadas por un valor implícito: el número de ítem de la prueba for u in range(7):
  • Aunque lo que manejamos son listas (y por eso las podemos modificar), la estructura principal es una tupla, ya que queremos que su estructura básica (la del "registro") sea invariable (3).
NOTAS
1 Como curiosidad, obsérvese que en ambos casos usamos corchetes para contener el índice.
2 Precisamente aquella en la que su rigidez se puede considerar una virtud.
3 Precisamente la limitación de este algoritmo es no haber conseguido reproducir la forma esperada en un diseño de base de datos: ser resultante de la unión o consulta de dos tablas: datos personales y resultados. Si obtengo un resultado mejor, lo publicaré.