En la lección anterior descubriste que una lista es una secuencia: elementos ordenados a los que se accede por índice. Lo que quizá no viste venir es que llevas usando otra secuencia desde el módulo 2 sin llamarla así. El texto —el tipo str— es exactamente eso: una secuencia de caracteres. Todo lo que aprendiste sobre índices, rebanadas y recorridos con for funciona igual sobre una cadena, con una diferencia decisiva: la cadena es inmutable.
Y esto no es un adorno teórico. En TareaFácil casi todo es texto: los títulos que teclea Marta, los nombres del equipo, las prioridades, las líneas que más adelante guardaremos en un fichero. Manejar bien las cadenas es lo que separa un programa que acepta « CARTEL feria » y «cartel Feria» como dos tareas distintas de uno que entiende que son la misma.
Contenido
- La cadena como secuencia inmutable
- Índices, rebanadas y recorrido
- Limpieza:
strip,lstrip,rstrip - Mayúsculas y minúsculas
- Búsqueda dentro del texto
- Comprobación del contenido
- Transformación y relleno
- Partición y unión: el puente con las listas
- Multilínea, escapes y cadenas crudas
- f-strings,
str()y Unicode - Comparar y ordenar textos
- Aplicación: títulos y códigos de tarea en Estudio Alba
- Errores comunes y consejos
- Ejercicios
- Conclusión
- La cadena como secuencia inmutable
Una cadena es una secuencia de caracteres con un orden fijo. len(), los índices y el operador in funcionan igual que en una lista:
titulo = "Cartel feria"
print(len(titulo)) # 12 (los espacios cuentan)
print(titulo[0], titulo[-1])# C l
print("feria" in titulo) # TrueLa diferencia está aquí:
Una cadena no se puede modificar. Ni un carácter. Todo lo que parece modificar texto en realidad crea una cadena nueva y deja la original intacta:
titulo = "cartel feria"
titulo.upper() # crea "CARTEL FERIA" y la tira: no se guarda
print(titulo) # cartel feria -> sigue igual
titulo = titulo.upper() # ahora si: reasignamos el nombre
print(titulo) # CARTEL FERIA| Lista | Cadena | |
|---|---|---|
Índices, rebanadas y for |
Sí | Sí |
| Modificar un elemento | Sí | No: TypeError |
| Métodos que modifican en el sitio | append, sort… |
Ninguno |
| Métodos que devuelven algo nuevo | sorted, copy |
Todos |
Retén esta idea, porque explica el error número uno con cadenas: si no guardas el resultado, no ha pasado nada. Y a cambio de la restricción hay una ventaja: al ser inmutables, las cadenas pueden usarse como claves de diccionario, cosa que verás en 05-03.
- Índices, rebanadas y recorrido
Todo lo de 05-01 se aplica sin cambios, salvo que aquí el resultado de una rebanada es otra cadena:
cliente = "Panaderia Sole"
print(cliente[0:9]) # Panaderia
print(cliente[-4:]) # Sole
print(cliente[:3].upper()) # PAN -> tres primeras letras en mayusculas
print(cliente[::-1]) # eloS airedanaP (del reves)Para recorrer carácter a carácter, el for directo; y con enumerate si necesitas la posición:
for letra in "Alba":
print(letra, end=" ") # A l b a
for i, letra in enumerate("Alba"):
print(f"{i}:{letra}", end=" ") # 0:A 1:l 2:b 3:aEl patrón contador de 03-02 funciona igual aquí: recorrer el texto sumando 1 cada vez que la letra cumple una condición. Casi siempre, eso sí, hay un método que lo resuelve en una línea —contar espacios a mano da lo mismo que titulo.count(" ")—, así que recorre carácter a carácter solo cuando la condición sea propia tuya.
- Limpieza:
strip, lstrip, rstrip
strip, lstrip, rstripCuando el texto viene de input() o de un fichero, casi siempre trae basura alrededor: espacios, tabuladores o el salto de línea final. La familia strip la quita.
| Método | Qué quita | Ejemplo |
|---|---|---|
.strip() |
Espacios de ambos extremos | " alba ".strip() → "alba" |
.lstrip() |
Solo por la izquierda | " alba".lstrip() → "alba" |
.rstrip() |
Solo por la derecha | "alba\n".rstrip() → "alba" |
.strip(chars) |
Cualquiera de esos caracteres | "**alba**".strip("*") → "alba" |
Con entrada = " Cartel feria del libro \n", f"[{entrada.strip()}]" imprime [Cartel feria del libro]. Ojo con un malentendido frecuente: .strip("abc") no borra la subcadena "abc", sino cualquier a, b o c que haya en los extremos. Y strip nunca toca el interior: " a b ".strip() deja "a b".
- Mayúsculas y minúsculas
| Método | Efecto | "cartel FERIA del libro" da |
|---|---|---|
.upper() |
Todo mayúsculas | CARTEL FERIA DEL LIBRO |
.lower() |
Todo minúsculas | cartel feria del libro |
.title() |
Primera letra de cada palabra | Cartel Feria Del Libro |
.capitalize() |
Primera letra de la cadena, resto en minúsculas | Cartel feria del libro |
.casefold() |
Como lower, pero más agresivo |
cartel feria del libro |
Los dos usos típicos son distintos y conviene no mezclarlos. Para comparar, se normaliza a minúsculas (nadie debería teclear «Marta» con la mayúscula exacta para que el programa lo acepte). Para mostrar, se elige la forma bonita:
respuesta = " MARTA "
print(respuesta.strip().lower() in ("marta", "luis", "nuria")) # True
print(respuesta.strip().capitalize()) # Marta.casefold() es la versión rigurosa de .lower() para comparaciones internacionales: en alemán convierte la ß en ss, cosa que .lower() no hace. Para español, ambas se comportan igual; usa .casefold() si comparas texto que puede venir en cualquier idioma.
- Búsqueda dentro del texto
| Método | Devuelve | Si no encuentra |
|---|---|---|
subcadena in texto |
True / False |
False |
.find(sub) |
Índice de la primera aparición | -1 |
.rfind(sub) |
Índice de la última aparición | -1 |
.index(sub) |
Índice de la primera aparición | ValueError |
.count(sub) |
Número de apariciones | 0 |
.startswith(pre) |
True si empieza así |
False |
.endswith(suf) |
True si termina así |
False |
titulo = "Cartel feria del libro, feria de otono"
print("feria" in titulo) # True -> lo mas legible si solo quieres saber si esta
print(titulo.find("feria")) # 7
print(titulo.rfind("feria")) # 23
print(titulo.find("cartel")) # -1 -> distingue mayusculas
print(titulo.count("feria")) # 2
print(titulo.startswith("Cartel")) # True
print(titulo.endswith((".png", ".pdf"))) # False -> admite una tupla de opcionesLa diferencia entre .find() y .index() es la misma que ya conoces de las listas: find devuelve -1 cuando no hay nada, mientras que index lanza ValueError. Usa find cuando «no estar» sea un caso normal, y recuerda que -1 es un índice válido en Python, así que nunca uses el resultado de find sin comprobar antes que no es -1.
- Comprobación del contenido
Estos métodos devuelven True o False y sirven para validar antes de convertir, tal como hacías en 02-04.
| Método | True cuando la cadena… |
|---|---|
.isdigit() |
Solo contiene dígitos (y no está vacía) |
.isalpha() |
Solo contiene letras |
.isalnum() |
Solo contiene letras o dígitos |
.isspace() |
Solo contiene espacios en blanco |
print("365".isdigit(), "3.5".isdigit(), "-3".isdigit()) # True False False
print("Nuria".isalpha(), "Nuria 1".isalpha()) # True False
print("ALB01".isalnum(), "".isalnum()) # True False
print(" ".isspace()) # TrueFíjate en las trampas: "3.5" no es isdigit (el punto no es un dígito), "-3" tampoco (el signo no lo es), y una cadena vacía devuelve False en todos ellos. Por eso if texto.isdigit(): antes de int(texto) sigue siendo la guarda correcta para enteros positivos, pero no vale para decimales ni negativos. La forma robusta de gestionar esos casos es try/except, y llega en Depuración y manejo de errores.
- Transformación y relleno
| Método | Qué hace | Ejemplo |
|---|---|---|
.replace(viejo, nuevo) |
Sustituye todas las apariciones | "a-b-c".replace("-", " ") → "a b c" |
.replace(v, n, cuenta) |
Solo las primeras cuenta |
"a-b-c".replace("-", " ", 1) → "a b-c" |
.zfill(n) |
Rellena con ceros por la izquierda | "7".zfill(3) → "007" |
.center(n, c) |
Centra en n caracteres |
"ALB".center(9, "-") → "---ALB---" |
.ljust(n, c) |
Alinea a la izquierda | "ALB".ljust(6, ".") → "ALB..." |
.rjust(n, c) |
Alinea a la derecha | "ALB".rjust(6, ".") → "...ALB" |
zfill es el atajo clásico para numerar: str(7).zfill(2) da "07", y así los códigos salen 01, 02, 03… en vez de 1, 2, 3. Los tres de alineación hacen lo mismo que los especificadores :<, :^ y :> de las f-strings; puedes usar cualquiera de las dos formas, aunque dentro de una f-string queda más limpio el especificador.
- Partición y unión: el puente con las listas
Aquí está la conexión directa con la lección anterior, y es de las cosas más útiles del lenguaje.
split(separador) parte una cadena y devuelve una lista de trozos. Sin argumentos, parte por espacios (agrupando los consecutivos).
linea = "Cartel feria;luis;alta;3"
print(linea.split(";")) # ['Cartel feria', 'luis', 'alta', '3'] -> 4 campos
print(" Cartel feria ".split()) # ['Cartel', 'feria']
print(linea.split(";", 1)) # ['Cartel feria', 'luis;alta;3'] -> solo el primer corte| Método | Qué hace |
|---|---|
.split(sep) |
Parte por sep de izquierda a derecha; sin sep, por espacios |
.rsplit(sep, n) |
Igual, pero cuenta los n cortes desde la derecha |
.splitlines() |
Parte por saltos de línea, sin dejar el \n |
join hace lo contrario: une los elementos de una lista en una sola cadena. Se llama sobre el separador, que es lo que despista al principio:
equipo = ["Marta", "Luis", "Nuria"]
print(", ".join(equipo)) # Marta, Luis, Nuria
print("".join(equipo)) # MartaLuisNuria
print(", ".join([str(d) for d in [3, 5, 2]])) # 3, 5, 2Se lee así: «coge la lista y pégala usando esta cadena como pegamento». La tercera línea recuerda que join solo funciona con listas de cadenas: si hay números, hay que convertirlos antes, normalmente con una comprensión de 05-01.
split y join son la pareja que convierte texto en datos y datos en texto. Cuando en 05-05 leamos un fichero de tareas, cada línea se convertirá en campos con split; y para escribirla, los campos volverán a ser una línea con join.
- Multilínea, escapes y cadenas crudas
Para un texto de varias líneas están las comillas triples, que respetan los saltos tal cual se escriben:
AYUDA = """TareaFacil - ayuda rapida
1 Registrar una tarea
2 Ver el listado completo
5 Salir"""
print(AYUDA)Dentro de una cadena normal, algunos caracteres se escriben con una secuencia de escape, que empieza por barra invertida:
| Escape | Significa |
|---|---|
\n |
Salto de línea |
\t |
Tabulador |
\\ |
Una barra invertida literal |
\" y \' |
Comilla doble o simple dentro de la cadena |
print("Marta\tLuis\tNuria") # separados por tabuladores
print("Ruta: C:\\alba\\tareas") # Ruta: C:\alba\tareas
print('Dijo "vale" y se fue') # comillas simples fuera, dobles dentro
print(r"C:\alba\tareas") # C:\alba\tareas -> cadena crudaLa última línea usa el prefijo r de cadena cruda (raw), que desactiva los escapes y resulta comodísimo con rutas de Windows. Cuidado con la trampa clásica: "C:\tareas" no es lo que parece, porque \t es un tabulador; con r"C:\tareas" no hay sorpresa.
- f-strings,
str() y Unicode
str() y UnicodeLas f-strings de 02-03 son la forma normal de construir texto con valores dentro, y sus especificadores de anchura son los que dan a TareaFácil sus tablas alineadas:
titulo, responsable, dias = "Cartel feria", "Marta", 3
print(f"{titulo:<24}{responsable:>10}{dias:>5}") # ancho fijo por columna
print(f"Media: {10 / 3:.2f}") # dos decimales: 3.33str(valor) convierte cualquier valor en su representación textual, y es imprescindible antes de concatenar con + o de usar join. Recuerda que "Dias: " + 3 da TypeError, mientras que "Dias: " + str(3) funciona y f"Dias: {3}" funciona sin conversión explícita.
Una idea sobre Unicode, que basta por ahora. Cada carácter de una cadena de Python es un carácter, no un byte: len("Panadería") vale 9 aunque la í lleve tilde, y "ñ"[0] es "ñ". Pero cuando ese texto sale del programa —a un fichero, a la red— hay que codificarlo en bytes, y ahí es donde entra UTF-8, la codificación estándar que sabe representar tildes, ñ, ç y prácticamente cualquier alfabeto. Si abres un fichero con la codificación equivocada verás cosas como PanaderÃa. Por eso en Guardar datos en archivos escribiremos siempre encoding="utf-8".
- Comparar y ordenar textos
Las cadenas se comparan con <, > y == carácter a carácter, según el número que cada carácter tiene asignado en la tabla Unicode. Y ahí aparece una sorpresa:
"Zapato" < "abeto" es True porque todas las mayúsculas van antes que todas las minúsculas: la Z es el carácter 90 y la a el 97. No es un capricho: es el orden de la tabla, no el del diccionario de la RAE. La consecuencia práctica es que sorted(["nuria", "Luis", "Marta"]) pone primero todo lo que empiece por mayúscula. La solución, que ya viste en 04-05 y 05-01, es ordenar por una clave normalizada:
print(sorted(["ana", "Zoe"])) # ['Zoe', 'ana'] -> mayusculas primero
print(sorted(["ana", "Zoe"], key=str.lower)) # ['ana', 'Zoe'] -> orden esperadoLo mismo vale para comparar: normaliza los dos lados antes de comparar. entrada.strip().lower() == "marta" acepta " Marta ", "MARTA" y "marta". Las tildes añaden otro escalón ("Alvaro" < "Álvaro"), y su tratamiento correcto requiere bibliotecas de intercalación que quedan fuera de este curso.
- Aplicación: títulos y códigos de tarea en Estudio Alba
Tres utilidades de texto para TareaFácil, escritas con lo de esta lección. La estructura del programa no cambia todavía —la tarea sigue siendo un puñado de variables hasta 05-03—, pero estas funciones entrarán en la sección de lógica.
def normalizar_titulo(texto):
"""Devuelve el titulo sin espacios sobrantes ni dobles, con la inicial en mayuscula."""
limpio = " ".join(texto.split()) # quita extremos y espacios repetidos
return limpio.capitalize()
def codigo_tarea(cliente, titulo, numero):
"""Devuelve un codigo tipo ALB-PAN-01 a partir del cliente y del titulo."""
siglas_cliente = cliente.strip().replace(" ", "")[:3].upper()
inicial = titulo.strip()[:1].upper()
return f"ALB-{siglas_cliente}-{str(numero).zfill(2)}{inicial}"
def partir_linea(linea):
"""Parte 'titulo;responsable;prioridad;dias' y devuelve los cuatro campos."""
campos = linea.strip().split(";")
if len(campos) != 4:
print("Linea mal formada:", linea)
return None
titulo, responsable, prioridad, dias = campos
return (normalizar_titulo(titulo), responsable.strip().capitalize(),
prioridad.strip().lower(), int(dias))Probémoslas:
print(normalizar_titulo(" cartel FERIA del libro ")) # Cartel feria del libro
print(codigo_tarea("Panaderia Sole", "Menu de temporada", 1)) # ALB-PAN-01M
print(partir_linea("Cartel feria;luis;alta;3")) # ('Cartel feria', 'Luis', 'alta', 3)Comentario de lo importante:
" ".join(texto.split())es el idioma clásico para colapsar espacios:split()sin argumentos parte por cualquier bloque de blancos y descarta los vacíos;joinlos vuelve a unir con exactamente un espacio. Resuelve de golpe los espacios de los extremos, los dobles y los tabuladores.cliente.replace(" ", "")[:3].upper()construye las siglas: quita los espacios, coge tres caracteres con una rebanada y los pasa a mayúsculas. Si el cliente tuviera menos de tres letras, la rebanada devuelve lo que haya sin error.str(numero).zfill(2)convierte primero a texto (zfilles un método de cadena, no de entero) y luego rellena con ceros.partir_lineadevuelveNonesi el número de campos no es el esperado, aplicando la cláusula de guarda de 04-02, y normaliza cada campo con las convenciones del programa: título capitalizado, responsable capitalizado, prioridad en minúsculas y días convertidos a entero.
Esa función partir_linea es, de hecho, media lección 05-05: leer un fichero de tareas consistirá en aplicarla a cada línea.
Errores Comunes y Consejos
Olvidar que los métodos no modifican. titulo.upper() sin asignar no hace nada visible; si el texto no cambia, comprueba que has escrito titulo = titulo.upper(). Y titulo[0] = "M" da TypeError, porque la cadena es inmutable: para cambiar un carácter hay que reconstruir, "M" + titulo[1:].
Usar el resultado de .find() sin comprobarlo. Si no encuentra nada devuelve -1, y texto[-1] es el último carácter, así que el programa seguirá adelante con un dato erróneo en vez de fallar.
Comparar sin normalizar. entrada == "marta" falla con " Marta ". Aplica .strip().lower() a los dos lados, siempre en el mismo sitio del programa.
Creer que .strip("hola") borra la palabra «hola». Borra cualquier h, o, l o a de los extremos. Para quitar una palabra concreta, .replace("hola", "").
join con números. ", ".join([3, 5]) lanza TypeError. Convierte antes: ", ".join([str(d) for d in dias]).
Consejo: normaliza en la entrada, formatea en la salida. Guarda los datos ya limpios (una sola forma canónica) y decide la presentación al imprimir. Así nunca tendrás dos versiones de «Cartel feria» conviviendo en la misma agenda.
Ejercicios
Ejercicio 1: Predecir la salida
Di qué imprime cada línea y explica por qué.
texto = " Menu Panaderia SOLE "
print(f"[{texto.strip().lower()}]")
print(texto.find("Sole"), texto.find("SOLE"))
print(texto.strip().split())
print("-".join(texto.split()))
print(texto.replace(" ", ""))
print("SOLE" < "Sole", "SOLE".isalpha())Ejercicio 2: Ficha de texto
Escribe resumen_titulo(titulo) que reciba un título y devuelva una cadena de una línea con: el título normalizado (sin espacios sobrantes y capitalizado), su número de caracteres, su número de palabras y sus iniciales en mayúsculas separadas por puntos. Para " cartel feria del libro " debe devolver Cartel feria del libro | 22 car. | 4 pal. | C.F.D.L.
Ejercicio 3: Lector de líneas del estudio
Dada esta lista de líneas, escribe un programa que las parta, descarte las mal formadas (que no tengan cuatro campos o cuyos días no sean un entero) e imprima una tabla alineada con título, responsable capitalizado, prioridad en mayúsculas y días. Al final, imprime el total de días de las líneas válidas.
lineas = [
"Cartel feria;luis;alta;3",
" menu panaderia sole ; MARTA ; media ; 5 ",
"Logotipo Vidal;nuria;baja",
"Flyer verano;marta;alta;dos",
]Soluciones
Solución 1.
[menu panaderia sole] -1 15 ['Menu', 'Panaderia', 'SOLE'] Menu-Panaderia-SOLE MenuPanaderiaSOLE True True
strip().lower() limpia y baja todo. find("Sole") da -1 porque la búsqueda distingue mayúsculas y en el texto pone SOLE; find("SOLE") sí lo encuentra, en el índice 15 contando los dos espacios iniciales. split() sin argumentos ignora los espacios sobrantes. join pega los trozos con guiones. replace(" ", "") elimina todos los espacios, también los interiores. Y "SOLE" < "Sole" es True porque la O mayúscula (79) va antes que la o minúscula (111).
Solución 2.
def resumen_titulo(titulo):
"""Devuelve una linea con el titulo normalizado y sus estadisticas."""
limpio = " ".join(titulo.split()).capitalize()
palabras = limpio.split()
iniciales = ".".join([p[0].upper() for p in palabras]) + "."
return f"{limpio} | {len(limpio)} car. | {len(palabras)} pal. | {iniciales}"
print(resumen_titulo(" cartel feria del libro "))
# Cartel feria del libro | 22 car. | 4 pal. | C.F.D.L.La clave está en calcular limpio una sola vez y medir sobre él: si contaras los caracteres del original obtendrías 26 por culpa de los espacios sobrantes. Las iniciales combinan una comprensión de 05-01 (p[0].upper() para cada palabra) con join, y el punto final se añade aparte porque join solo pone separadores entre elementos.
Solución 3.
total = 0
print(f"{'TITULO':<24}{'RESP.':>10}{'PRIOR.':>10}{'DIAS':>6}")
for linea in lineas:
campos = linea.strip().split(";")
if len(campos) != 4 or not campos[3].strip().isdigit():
print(f"Descartada: {linea.strip()}")
continue
titulo = " ".join(campos[0].split()).capitalize()
responsable = campos[1].strip().capitalize()
prioridad = campos[2].strip().upper()
dias = int(campos[3].strip())
total += dias
print(f"{titulo:<24}{responsable:>10}{prioridad:>10}{dias:>6}")
print(f"{'TOTAL':<24}{total:>26}")Salen dos filas válidas —«Cartel feria / Luis / ALTA / 3» y «Menu panaderia sole / Marta / MEDIA / 5», total 8 días— y dos descartes: la tercera línea solo tiene tres campos y la cuarta trae "dos" donde debería ir un número. Dos detalles que valen para cualquier lector de datos. La comprobación len(campos) != 4 or not campos[3].strip().isdigit() reúne las dos causas de descarte en una sola guarda, y el continue de 03-03 evita anidar el resto del cuerpo dentro de un if. Y cada campo se limpia con .strip() antes de usarlo, porque la segunda línea trae espacios alrededor de los puntos y comas: sin ese strip, " 5 ".isdigit() daría False y la línea buena se descartaría.
Conclusión
Una cadena es una secuencia inmutable de caracteres: acepta índices, rebanadas y recorridos con for igual que una lista, pero no se puede modificar, así que todos sus métodos devuelven una cadena nueva y hay que guardarla. Has recorrido las seis familias de métodos: limpieza (strip, lstrip, rstrip), mayúsculas y minúsculas (upper, lower, title, capitalize, casefold), búsqueda (in, find, rfind, index, count, startswith, endswith), comprobación (isdigit, isalpha, isalnum, isspace), transformación (replace, zfill, center, ljust, rjust) y partición y unión (split, rsplit, splitlines, join), que es el puente entre el texto y las listas. Sabes escribir textos multilínea con """, protegerte de los escapes con r"", alinear columnas con f-strings, y por qué "Zapato" < "abeto" obliga a comparar y ordenar sobre una versión en minúsculas. Y sabes que las tildes y la ñ son un carácter cada una, pero que al salir a disco el texto se codifica en bytes y por eso UTF-8 importa.
En Estudio Alba ya puedes normalizar los títulos para que « CARTEL feria » y «Cartel Feria» sean el mismo texto, generar códigos como ALB-PAN-01M y convertir una línea "Cartel feria;luis;alta;3" en cuatro campos listos para usar. Lo que sigue sin resolverse es el problema que 05-01 dejó abierto: esos cuatro campos siguen siendo cuatro variables sueltas, o peor, cuatro listas paralelas que hay que mantener sincronizadas a mano. La solución llega en Diccionarios y conjuntos: una estructura donde cada dato se guarda con su nombre —titulo, responsable, prioridad, dias, completada— en lugar de con un número de posición. Ahí TareaFácil dará el salto a la v0.9 y una tarea pasará a ser, por fin, una sola cosa.
Fundamentos de la Programación
Módulo 1: Introducción a la Programación
- ¿Qué es la programación?
- Historia de la programación
- Lenguajes de programación
- Entornos de desarrollo
- Del problema al algoritmo
Módulo 2: Conceptos Básicos
- Variables y tipos de datos
- Operadores y expresiones
- Entrada y salida de datos
- Conversión de tipos y validación de datos
Módulo 3: Estructuras de Control
Módulo 4: Funciones y Procedimientos
- Definición y uso de funciones
- Parámetros y retorno de valores
- Ámbito de variables
- Descomponer un programa en funciones
- Funciones como valores: lambda y orden superior
Módulo 5: Estructuras de Datos
- Listas y arreglos
- Cadenas de caracteres
- Diccionarios y conjuntos
- Tuplas y estructuras anidadas
- Guardar datos en archivos: texto, CSV y JSON
Módulo 6: Algoritmos Básicos
Módulo 7: Objetos y Organización del Código
- De los datos a los objetos: clases e instancias
- Atributos, métodos y constructor
- Colecciones de objetos
- Módulos, paquetes e importaciones
Módulo 8: Buenas Prácticas y Herramientas
- Documentación y comentarios
- Depuración y manejo de errores
- Control de versiones
- Pruebas automatizadas
- Estilo, legibilidad y refactorización
