Strings en Python — ejercicios con correos de la universidad, firmas con iniciales y un parte de asistencia (y los dos espacios que rompieron mis iniciales)
Quería sacar las iniciales de cada alumno de la lista para ponerlas en las etiquetas de los grupos de prácticas. Tres líneas: partir el nombre por los espacios con split(" ") y quedarme con la primera letra de cada trozo. Con «Ana García» funcionó. Con el siguiente nombre de la lista, Python me soltó IndexError: string index out of range.
«¿Fuera de rango? Si solo pido la primera letra.» El nombre venía de un formulario y tenía dos espacios entre «Ana» y «García». split(" ") corta en cada espacio, así que entre los dos dejó un trozo vacío, "", y pedirle la primera letra a un string vacío es pedir algo que no existe. Con split(), sin argumento, no habría pasado nada. Es el segundo fragmento de «¿Qué falla aquí?», y no será el último fallo de espacios que veas.
Hoy practicamos con tres ejercicios de dificultad creciente, tres fragmentos para depurar y un chuletario para llevarte. Estos ejercicios usan la teoría de strings y repasan lo de la práctica. Puedes hacerlos sin ellas, pero te costará más.
Tabla de Contenidos
Cómo usar este artículo
- Lee el enunciado de un nivel y no mires la solución todavía.
- Escribe tu versión, ejecútala y compara tu salida con la esperada.
- Si te atascas, mira solo la 🔵 pista y vuelve a intentarlo.
- Cuando termines, compara con la solución y fíjate en por qué está hecha así, no solo en que dé la misma salida.
Ningún ejercicio usa input(): los datos van fijos en el código. Cuando compares tu salida con la esperada, fíjate también en los espacios: en los ejercicios de strings, un espacio de más es una diferencia de verdad.
Nivel 1 · Básico: la ficha de un correo
Enunciado. Parte de este correo, copiado de un formulario con espacios y mayúsculas de más:
correo = " Maria.Jose.Nunez@ALU.ULPGC.ES "
Haz, en este orden:
- Quita los espacios de los extremos, pásalo a minúsculas y guárdalo en la misma variable. Muéstralo.
- Muestra su longitud.
- Separa el usuario (lo de antes de la
@) y el dominio (lo de después) y muéstralos. - Muestra si es un correo de alumno, es decir, si termina en
@alu.ulpgc.es. - Muestra cuántos puntos tiene el usuario.
- Construye el nombre para mostrar: el usuario con los puntos cambiados por espacios y cada palabra con mayúscula inicial.
- Muestra las iniciales de ese nombre, todas juntas.
- Muestra el correo oculto: la primera letra del usuario, un asterisco por cada letra restante del usuario, y luego la
@y el dominio.
Salida esperada:
maria.jose.nunez@alu.ulpgc.es Longitud: 29 Usuario: maria.jose.nunez Dominio: alu.ulpgc.es ¿Es de alumno? True Puntos en el usuario: 2 Nombre: Maria Jose Nunez Iniciales: MJN Oculto: m***************@alu.ulpgc.es
Para el paso 3, partition("@"). Para el 6, replace y title(). Para el 8, piensa en cuántos asteriscos hacen falta (len(usuario) - 1) y en que "*" * n repite el asterisco n veces.
Nivel 2 · Intermedio: firmas y códigos de alumno
Enunciado. Tienes los nombres completos de cuatro alumnos, escritos de cualquier manera. En todos, las dos últimas palabras son los apellidos y lo anterior, el nombre (que puede ser compuesto):
ALUMNOS = [
"ana garcía pérez",
"LUIS ROMERO SANTANA",
"maría josé núñez torre",
" marta gil sosa ",
]
Escribe tres funciones:
firma(nombre_completo): la inicial de cada nombre seguida de un punto y los apellidos completos, todo con mayúscula inicial."maría josé núñez torre"→"M. J. Núñez Torre".codigo(nombre_completo, n): las tres primeras letras de cada apellido en mayúsculas, separadas por guiones, y el número de listancon dos cifras. Para el tercer alumno,"NÚÑ-TOR-03".contar_vocales(texto): cuántas vocales tiene el texto, con o sin tilde y sin importar las mayúsculas.
Muestra una tabla con la firma (22 caracteres, a la izquierda), el código (14, a la izquierda) y el número de vocales (7, a la derecha), con su cabecera.
Salida esperada:
Firma Código Vocales A. García Pérez GAR-PÉR-01 7 L. Romero Santana ROM-SAN-02 8 M. J. Núñez Torre NÚÑ-TOR-03 9 M. Gil Sosa GIL-SOS-04 5
Después de title() y split() tienes una lista de palabras, y a una lista también se le pueden aplicar slices: palabras[-2:] son las dos últimas y palabras[:-2], todas las demás. Para contar vocales, recorre el texto letra a letra y pregunta con in si cada una está en un string con todas las vocales.
Nivel 3 · Desafío: el parte de asistencia
Enunciado. El parte de asistencia de las prácticas está en un string de varias líneas. Cada línea con datos tiene una fecha, dos puntos y los nombres de los que vinieron, separados por comas (escritos como cada uno quiso). Las líneas vacías y las que empiezan por # hay que saltarlas:
PARTE = """ # Asistencia a las prácticas de FP1 (subgrupo 43) 15/09: Ana, Luis, María José, Marta 22/09: ana , Marta,MARÍA JOSÉ 29/09: Luis, María José, marta # 06/10: festivo 13/10: Ana, María José, Marta, Pablo """
Escribe dos funciones:
leer_parte(texto): devuelve una tupla con la lista de fechas de las sesiones y un diccionario nombre → conjunto de fechas a las que vino. Los nombres tienen que quedar normalizados (" MARÍA JOSÉ "→"María José"), para que la misma persona no cuente como dos.fila(nombre, fechas, vino): devuelve la fila del informe de un alumno: el nombre en 12 caracteres, unaXpor cada sesión a la que vino y un·por cada falta (separadas por espacios), las sesiones como3/4y el porcentaje en 5 caracteres. Si no llega al 75 %, añade el aviso<- no llega al 75 %.
Muestra una línea con los días de las sesiones y la fila de cada alumno, en orden alfabético.
Salida esperada:
Sesiones: 15 22 29 13 Ana X X · X 3/4 75% Luis X · X · 2/4 50% <- no llega al 75 % Marta X X X X 4/4 100% María José X X X X 4/4 100% Pablo · · · X 1/4 25% <- no llega al 75 %
texto.splitlines() te da una lista con las líneas. Para cada una: strip(), saltar si está vacía o si startswith("#"), y partition(":") para separar la fecha de la lista de nombres. Los nombres se limpian con " ".join(nombre.split()).title(). Y la fila de marcas es un join de "X" o "·" según si la fecha está en el conjunto del alumno.
¿Qué falla aquí?
Tres fragmentos con un fallo cada uno. Antes de leer la explicación, haz dos cosas: predice si da error y, si no lo da, qué imprime.
Fragmento 1
limpiar.py
nombres = [" ana", "LUIS ", " marta "]
for nombre in nombres:
nombre = nombre.strip().title() # limpiar cada nombre de la lista
print(nombres)
*Piénsalo antes de seguir: queremos la lista de nombres limpia. ¿Qué imprime python3 limpiar.py?*
Explicación. No da error, pero la lista sale igual que estaba:
[' ana', 'LUIS ', ' marta ']
En cada vuelta, nombre recibe uno de los strings de la lista. La línea nombre = nombre.strip().title() crea un string nuevo y hace que la variable nombre apunte a él… pero eso no afecta a la lista: la lista sigue guardando los strings originales, que no han cambiado (no pueden: son inmutables). En la vuelta siguiente, nombre pasa al siguiente elemento y el string limpio se pierde.
Pensar que reasignar la variable del for cambia la lista. Con números o strings nunca funciona: la variable del bucle es solo una etiqueta que va pasando de un elemento a otro.
Arreglo. Construye una lista nueva con los strings limpios:
nombres = [" ana", "LUIS ", " marta "]
limpios = []
for nombre in nombres:
limpios.append(nombre.strip().title()) # el string nuevo se GUARDA en otra lista
print(limpios)
Al ejecutarlo:
['Ana', 'Luis', 'Marta']
Con una list comprehension queda en una línea: nombres = [n.strip().title() for n in nombres]. Aquí sí cambia lo que guarda nombres, porque la asignación es a la variable de la lista.
Fragmento 2
iniciales.py
nombre = "Ana García" # copiado de un formulario (con dos espacios)
partes = nombre.split(" ")
iniciales = ""
for p in partes:
iniciales += p[0]
print(iniciales)
¿Da error? ¿Qué imprime? Queremos las iniciales del nombre.
Explicación. Es el fallo del principio:
Traceback (most recent call last):
File "iniciales.py", line 5, in <module>
iniciales += p[0]
~^^^
IndexError: string index out of range
split(" ") corta en cada espacio. Entre los dos espacios seguidos no hay nada, así que la lista queda ['Ana', '', 'García']. En la segunda vuelta, p es el string vacío y p[0] pide la posición 0 de algo que no tiene ninguna posición: IndexError.
Usar split(" ") para separar palabras escritas por personas. Los espacios dobles (y los tabuladores) son mucho más frecuentes de lo que parece, sobre todo en datos copiados de formularios u hojas de cálculo.
Arreglo. split() sin argumento trata cualquier hueco como un solo separador:
nombre = "Ana García"
partes = nombre.split() # sin argumento: los huecos repetidos no crean trozos vacíos
iniciales = ""
for p in partes:
iniciales += p[0]
print(partes, iniciales)
Esto es lo que imprime:
['Ana', 'García'] AG
Si de verdad necesitas cortar por un espacio exacto (porque los espacios vacíos significan algo, como columnas en blanco), puedes filtrar los trozos vacíos: [p for p in nombre.split(" ") if p].
Fragmento 3
notas.py
notas = [7.456, 10.0, 0.5]
for nota in notas:
print(f"Nota: {nota:.2}") # quiero dos decimales
¿Da error? ¿Qué imprime? Queremos mostrar cada nota con dos decimales.
Explicación. No da error, pero la salida no es la que queríamos:
Nota: 7.5 Nota: 1e+01 Nota: 0.5
:.2 **sin la f no significa «dos decimales», sino «dos cifras significativas**». Con 7.456 las dos cifras son 7 y 5 (redondeando): 7.5. Con 10.0 las dos cifras son 1 y 0, y para escribir «10» con dos cifras significativas Python usa notación científica: 1e+01 (1 × 10¹). Con 0.5 basta una cifra, así que sale 0.5, que parece correcto… por casualidad.
Olvidar la f del formato. El fallo no siempre se ve: con algunos valores el resultado parece correcto y con otros sale notación científica.
Arreglo. Con .2f, dos decimales fijos; y con un ancho de 5, las notas quedan además alineadas:
notas = [7.456, 10.0, 0.5]
for nota in notas:
print(f"Nota: {nota:5.2f}") # .2f: dos decimales; 5: ancho para alinear
Salida:
Nota: 7.46 Nota: 10.00 Nota: 0.50
7.456 se redondea a 7.46, y 0.5 se completa con ceros hasta 0.50. El ancho 5 añade un espacio delante de las notas de una cifra para que 10.00 y 7.46 terminen en la misma columna.
Soluciones
Mira esta sección solo cuando hayas intentado el ejercicio. Si tu código es distinto pero da la misma salida, también es una solución válida.
Solución del Nivel 1
correo.py
correo = " Maria.Jose.Nunez@ALU.ULPGC.ES "
correo = correo.strip().lower() # 1. limpiar y pasar a minúsculas (y guardarlo)
print(correo)
print("Longitud:", len(correo)) # 2. longitud
usuario, _, dominio = correo.partition("@") # 3. trozos antes y después de la @
print("Usuario:", usuario)
print("Dominio:", dominio)
print("¿Es de alumno?", correo.endswith("@alu.ulpgc.es")) # 4.
print("Puntos en el usuario:", usuario.count(".")) # 5.
nombre = usuario.replace(".", " ").title() # 6. los puntos pasan a ser espacios
print("Nombre:", nombre)
iniciales = ""
for palabra in nombre.split(): # 7. la primera letra de cada palabra
iniciales += palabra[0]
print("Iniciales:", iniciales)
oculto = usuario[0] + "*" * (len(usuario) - 1) + "@" + dominio # 8. ocultar el usuario
print("Oculto:", oculto)
Por qué está hecho así:
- El paso 1 reasigna
correo. Si escribieras solocorreo.strip().lower(), todo lo demás trabajaría con el correo sucio:endswith("@alu.ulpgc.es")daríaFalsepor las mayúsculas y los espacios del final. partition("@")devuelve tres trozos; la@no nos hace falta y se recoge en_, el nombre que se suele usar en Python para «variable que no voy a usar».- La longitud es 29: 16 caracteres del usuario (
maria.jose.nunez) + 1 de la@+ 12 del dominio (alu.ulpgc.es). Cuéntalo: 16 + 1 + 12 = 29. count(".")sobre el usuario da 2. Sobre el correo entero daría 4, porque el dominio tiene otros dos.usuario.replace(".", " ")da"maria jose nunez", ytitle()pone las mayúsculas:"Maria Jose Nunez".- Las iniciales se acumulan con
+=recorriendonombre.split():"M","MJ","MJN". - En el correo oculto,
"*" * (len(usuario) - 1)son 16 − 1 = 15 asteriscos. El paréntesis es necesario: sin él,"*" * len(usuario) - 1intentaría restar 1 a un string y daríaTypeError.
Solución del Nivel 2
firmas.py
ALUMNOS = [
"ana garcía pérez",
"LUIS ROMERO SANTANA",
"maría josé núñez torre",
" marta gil sosa ",
]
VOCALES = "aeiouáéíóúü"
def firma(nombre_completo):
"""'maría josé núñez torre' -> 'M. J. Núñez Torre' (las dos últimas palabras son los apellidos)."""
palabras = nombre_completo.title().split()
nombres, apellidos = palabras[:-2], palabras[-2:] # slices de la LISTA de palabras
iniciales = " ".join(p[0] + "." for p in nombres)
return iniciales + " " + " ".join(apellidos)
def codigo(nombre_completo, n):
"""Tres primeras letras de cada apellido, en mayúsculas, y el número con dos cifras."""
ap1, ap2 = nombre_completo.upper().split()[-2:]
return ap1[:3] + "-" + ap2[:3] + "-" + str(n).zfill(2)
def contar_vocales(texto):
cuenta = 0
for letra in texto.lower(): # lower: así 'A' y 'a' cuentan igual
if letra in VOCALES: # in sobre un string: ¿es uno de sus caracteres?
cuenta += 1
return cuenta
print(f"{'Firma':<22}{'Código':<14}Vocales")
for n, alumno in enumerate(ALUMNOS, start=1):
print(f"{firma(alumno):<22}{codigo(alumno, n):<14}{contar_vocales(alumno):>7}")
Por qué está hecho así:
firmaempieza contitle().split().split()sin argumento ya se ocupa de los espacios de sobra del cuarto alumno, así que no hace faltastrip().- Los slices funcionan igual en listas que en strings. Con
"maría josé núñez torre":
| Expresión | Tipo | Valor |
|---|---|---|
nombre_completo.title().split() | list | ['María', 'José', 'Núñez', 'Torre'] |
palabras[:-2] | list | ['María', 'José'] |
palabras[-2:] | list | ['Núñez', 'Torre'] |
" ".join(p[0] + "." for p in nombres) | str | 'M. J.' |
| resultado | str | 'M. J. Núñez Torre' |
- Al usar
[:-2]en lugar de[0], la misma función sirve para nombres simples (Ana, una inicial) y compuestos (María José, dos). La alternativa obvia,palabras[0][0] + ". ", perdería la segunda inicial. codigodesempaqueta los dos apellidos conap1, ap2 = …[-2:]y toma sus tres primeras letras con[:3]. Como un slice nunca da error, un apellido de solo dos letras, comoLi, saldría completo (LI) sin romper nada.str(n).zfill(2)convierte 3 en"03". Con una f-string también valdríaf"{n:02d}".contar_vocalespasa el texto a minúsculas una sola vez, antes del bucle, y pregunta coninsi cada letra está enVOCALES. Con un string,inmira si es uno de sus caracteres (o un trozo seguido). Para"ana garcía pérez": a, a, a, í, a, é, e = 7.- La cabecera usa los mismos anchos que las filas; «Vocales» mide justo 7, así que queda encima de los números alineados a la derecha.
El código conserva las tildes (PÉR, NÚÑ). Si el código tuviera que ir a un sistema que no las acepta, bastaría con pasarlo por la función sin_tildes de la práctica.
Solución del Nivel 3
asistencia.py
PARTE = """
# Asistencia a las prácticas de FP1 (subgrupo 43)
15/09: Ana, Luis, María José, Marta
22/09: ana , Marta,MARÍA JOSÉ
29/09: Luis, María José, marta
# 06/10: festivo
13/10: Ana, María José, Marta, Pablo
"""
MINIMO = 0.75 # hace falta venir al 75 % de las sesiones
def leer_parte(texto):
"""Devuelve la lista de fechas y un diccionario nombre -> conjunto de fechas a las que vino."""
fechas = []
asistencias = {}
for linea in texto.splitlines(): # una línea cada vez
linea = linea.strip()
if linea == "" or linea.startswith("#"): # se saltan las vacías y los comentarios
continue
fecha, _, lista = linea.partition(":")
fechas.append(fecha)
for nombre in lista.split(","):
nombre = " ".join(nombre.split()).title() # ' MARÍA JOSÉ ' -> 'María José'
if nombre not in asistencias:
asistencias[nombre] = set()
asistencias[nombre].add(fecha)
return fechas, asistencias
def fila(nombre, fechas, vino):
"""Una fila del informe: una X por sesión a la que vino y un punto por cada falta."""
marcas = " ".join("X" if f in vino else "·" for f in fechas)
pct = len(vino) / len(fechas)
aviso = "" if pct >= MINIMO else " <- no llega al 75 %"
return f"{nombre:<12}{marcas} {len(vino)}/{len(fechas)} {pct:>5.0%}{aviso}"
fechas, asistencias = leer_parte(PARTE)
print(f"{'Sesiones:':<12}{' '.join(f[:2] for f in fechas)}") # solo el día: 15 22 29 13
for nombre in sorted(asistencias):
print(fila(nombre, fechas, asistencias[nombre]))
Este es el resultado dibujado: cada fila es el conjunto de fechas de un alumno, y en coral quienes no llegan al 75 %:
Por qué está hecho así:
splitlines()parte el texto en líneas. Como el string empieza con un salto de línea (justo después de las"""), la primera línea está vacía: por eso es importante saltar las líneas vacías, y no solo los comentarios.- Cada línea se limpia con
strip()antes de las comprobaciones. Así, una línea con solo espacios también cuenta como vacía, y un comentario con espacios delante también empieza por#.
Línea (tras strip) | ¿Qué pasa? | fechas después |
|---|---|---|
"" | Vacía: se salta | [] |
"# Asistencia…" | Comentario: se salta | [] |
"15/09: Ana, Luis, María José, Marta" | Se lee | ['15/09'] |
"22/09: ana , Marta,MARÍA JOSÉ" | Se lee | ['15/09', '22/09'] |
"" | Vacía: se salta | |
"29/09: Luis, María José, marta" | Se lee | […, '29/09'] |
"# 06/10: festivo" | Comentario: se salta | |
"13/10: Ana, María José, Marta, Pablo" | Se lee | […, '13/10'] (4 sesiones) |
partition(":")separa la fecha de la lista.lista.split(",")da trozos como" ana "o"MARÍA JOSÉ", y" ".join(nombre.split()).title()los deja todos con la misma forma. Sin esa normalización,"ana"y"Ana"serían dos alumnos distintos en el diccionario.- Cada alumno tiene un conjunto de fechas: si alguien apareciera dos veces en la misma línea, el conjunto no lo contaría dos veces.
- En
fila," ".join("X" if f in vino else "·" for f in fechas)recorre las sesiones en orden y elige una marca para cada una. Por eso se recorren lasfechas(que es una lista ordenada) y no el conjuntovino(que no tiene orden). - El porcentaje es
len(vino) / len(fechas): Luis vino a 2 de 4, 2 / 4 = 0,5, y:>5.0%lo escribe como50%en 5 caracteres. Ana, 3 / 4 = 0,75, justo el mínimo: como la condición espct >= MINIMO, no lleva aviso. f[:2]saca el día de cada fecha ("15/09"→"15") para la línea de sesiones.sorted(asistencias)ordena los nombres, y aquí hay una sorpresa: Marta sale antes que María José.sortedcompara carácter a carácter según el código Unicode: las dos empiezan por «Mar», y luego latva antes que laícon tilde, que tiene un código mayor que todas las letras sin tilde.
Míralo paso a paso en Python Tutor
El fallo del fragmento 2 se entiende mucho mejor en vivo. Python Tutor ejecuta el código línea a línea y dibuja las listas como cajas con sus elementos. Elige Python 3 y pega este programa:
nombre = "Ana García"
partes = nombre.split(" ") # con dos espacios: aparece un trozo vacío
print(partes)
bien = nombre.split() # sin argumento: solo las palabras
print(bien)
iniciales = ""
for p in bien:
iniciales += p[0]
print(iniciales)
Salida esperada:
['Ana', '', 'García'] ['Ana', 'García'] AG
Qué observar, paso a paso:
- Después de la línea 2,
partesse dibuja como una lista de tres casillas. La del medio está vacía: es el string""que aparece entre los dos espacios. - Después de la línea 4,
bientiene solo dos casillas:split()se ha comido los dos espacios como si fueran uno. - En el bucle, fíjate en cómo
pva tomando"Ana"y luego"García", y cómoinicialespasa de""a"A"y a"AG": cada+=crea un string nuevo. - Cambia
bienporpartesen elfory vuelve a ejecutar: verás exactamente en qué paso salta elIndexError(cuandopes el string vacío).
Chuletario: Strings en Python
Lo esencial en 30 segundos
- Un string es una secuencia inmutable de caracteres. Ningún método lo cambia: todos devuelven uno nuevo que hay que guardar.
- Índices desde 0; negativos desde el final.
s[-1]es el último. Fuera de rango →IndexError. - **Slices
s[a:b]:** deaabsin incluirb. Nunca dan error.s[::-1]le da la vuelta. - **
split()para palabras**,split(sep)para campos,sep.join(lista)para juntar. - Limpiar:
strip(),lower()/title(),replace(",", ".")antes defloat. - Formato:
f"{x:<10}",f"{x:>8.2f}",f"{n:03d}",f"{p:.0%}".
Crear, medir y recorrer
| Quiero… | Código | Resultado |
|---|---|---|
| Un string | "Ana" o 'Ana' | 'Ana' |
| Varias líneas | """línea 1\nlínea 2""" | String con salto de línea |
| Sin escapes (rutas) | r"C:\notas" | Las barras se quedan |
| Longitud | len("Núñez") | 5 |
| Primer / último carácter | s[0], s[-1] | Un string de 1 carácter |
| Un trozo | "03/09/2007"[3:5] | '09' |
| Los últimos 4 | fecha[-4:] | '2007' |
| Al revés | s[::-1] | String invertido |
| Recorrer letra a letra | for letra in s: | Un carácter por vuelta |
| Unir | "Ana" + " " + "Gil" | 'Ana Gil' |
| Repetir | "-" * 20 | 20 guiones |
| Número a string | str(7.5) | '7.5' |
Transformar, buscar y comprobar
| Quiero… | Código | Resultado |
|---|---|---|
| Quitar espacios de los extremos | " Ana ".strip() | 'Ana' |
| Un solo espacio entre palabras | " ".join(s.split()) | Espacios normalizados |
| Mayúsculas / minúsculas | s.upper(), s.lower() | |
| Mayúscula en cada palabra | "ana gil".title() | 'Ana Gil' |
| Comparar sin mayúsculas | a.lower() == b.lower() | True / False |
| Cambiar trozos | "7,5".replace(",", ".") | '7.5' |
| ¿Está? | "@" in correo | True / False |
| ¿Dónde? (puede no estar) | correo.find("@") | Posición o -1 |
| ¿Dónde? (tiene que estar) | correo.index("@") | Posición o ValueError |
| Contar | s.count(".") | Entero |
| Empieza / termina | s.startswith("ana"), s.endswith(".es") | True / False |
| Partir en 3 por el primero | "ana@ulpgc.es".partition("@") | ('ana', '@', 'ulpgc.es') |
| Partir en trozos | "a;b;c".split(";") | ['a', 'b', 'c'] |
| Juntar trozos | "/".join(["03", "09"]) | '03/09' |
| ¿Solo cifras? | "2007".isdigit() | True |
| ¿Solo letras? | "Ana".isalpha() | True |
| Ceros a la izquierda | "7".zfill(3) | '007' |
Formato con f-strings
| Quiero… | Código | Resultado |
|---|---|---|
| Meter un valor | f"Nota: {nota}" | Nota: 7.456 |
| 2 decimales | f"{nota:.2f}" | 7.46 |
| Texto a la izquierda en 10 | f"[{'Ana':<10}]" | [Ana ] |
| Número a la derecha en 6 | f"[{7.5:6.2f}]" | [ 7.50] |
| Centrado con relleno | f"{'FP1':=^9}" | ===FP1=== |
| Entero con ceros | f"{7:03d}" | 007 |
| Porcentaje | f"{0.75:.0%}" | 75% |
| Una llave literal | f"{{x}}" | {x} |
Plantillas de los ejercicios
Limpiar un campo escrito a mano (Niveles 1, 2 y 3):
texto = " ".join(texto.split()).title() # espacios normalizados y mayúsculas correo = correo.strip().lower() # ¡guardando el resultado!
Partir y desempaquetar (Niveles 1 y 3):
usuario, _, dominio = correo.partition("@")
fecha, _, lista = linea.partition(":")
nombres = [n.strip() for n in lista.split(",")]
Recorrer las líneas de un texto, saltando vacías y comentarios (Nivel 3):
for linea in texto.splitlines():
linea = linea.strip()
if linea == "" or linea.startswith("#"):
continue
# ... procesar la línea
Tabla alineada (Niveles 2 y 3):
print(f"{'Nombre':<12}{'Nota':>6}") # mismos anchos en la cabecera...
for nombre, nota in datos:
print(f"{nombre:<12}{nota:>6.2f}") # ...y en cada fila
Errores que más se repiten
| Síntoma | Causa probable |
|---|---|
El string «no cambia» después de strip(), upper()… | No guardaste el resultado: s = s.strip() |
La lista sigue igual tras un for que «la limpia» | Reasignas la variable del bucle; crea una lista nueva |
TypeError: 'str' object does not support item assignment | Intentas s[0] = …; construye otro string: "A" + s[1:] |
IndexError: string index out of range | Índice ≥ len(s), o un string vacío (muchas veces por split(" ")) |
TypeError: can only concatenate str (not "int") to str | "texto" + número: usa str() o una f-string |
ValueError: could not convert string to float: '7,5' | Coma decimal: float(s.replace(",", ".")) |
ValueError: substring not found | index no encuentra el trozo: usa find o comprueba con in |
Un trozo vacío '' en la lista de split | Separador repetido o al principio/final: usa split() o filtra |
1e+01 en lugar de 10.00 | Formato .2 sin f: usa .2f |
| La tabla se descoloca | Anchos distintos en cabecera y filas, o un valor más largo que su ancho |
"Ana" == "ana" da False | Mayúsculas distintas: compara con .lower() en los dos lados |
| Una ruta de Windows sale rota | \n o \t dentro de la ruta: usa r"C:\…" |
Antes de entregar, pregúntate
- ¿Guardo en una variable todo lo que devuelven los métodos de string?
- ¿Separo las palabras con
split()sin argumento y limpio cada trozo constrip()? - ¿Compruebo la longitud (o que el string no esté vacío) antes de usar índices?
- ¿Mis f-strings usan los mismos anchos en la cabecera y en las filas, y
.2f(con laf) para los decimales?
Si quieres repasar el tema desde la base, vuelve a la teoría de strings.
Sigue aprendiendoTodos los artículos de Python¿Te ha servido este artículo? Así puedes apoyar SergioLearns →
