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.

Cómo usar este artículo

  1. Lee el enunciado de un nivel y no mires la solución todavía.
  2. Escribe tu versión, ejecútala y compara tu salida con la esperada.
  3. Si te atascas, mira solo la 🔵 pista y vuelve a intentarlo.
  4. 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:

  1. Quita los espacios de los extremos, pásalo a minúsculas y guárdalo en la misma variable. Muéstralo.
  2. Muestra su longitud.
  3. Separa el usuario (lo de antes de la @) y el dominio (lo de después) y muéstralos.
  4. Muestra si es un correo de alumno, es decir, si termina en @alu.ulpgc.es.
  5. Muestra cuántos puntos tiene el usuario.
  6. Construye el nombre para mostrar: el usuario con los puntos cambiados por espacios y cada palabra con mayúscula inicial.
  7. Muestra las iniciales de ese nombre, todas juntas.
  8. 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 lista n con 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, una X por cada sesión a la que vino y un · por cada falta (separadas por espacios), las sesiones como 3/4 y 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.

split(" ") frente a split()El string "Ana García" tiene dos espacios seguidos. split(" ") corta en cada espacio y entre los dos deja un trozo vacío: ["Ana", "", "García"]. split() sin argumento trata cualquier hueco como un solo separador: ["Ana", "García"].split(" ") frente a split()Ana␣␣García34nombre.split(" ")"Ana""""García"trozo vacío: ""[0] → IndexErrornombre.split()"Ana""García"
Con split(" ") cada espacio es un corte, y entre dos cortes seguidos queda un string vacío al que no se le puede pedir [0].

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 solo correo.strip().lower(), todo lo demás trabajaría con el correo sucio: endswith("@alu.ulpgc.es") daría False por 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", y title() pone las mayúsculas: "Maria Jose Nunez".
  • Las iniciales se acumulan con += recorriendo nombre.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) - 1 intentaría restar 1 a un string y daría TypeError.

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í:

  • firma empieza con title().split(). split() sin argumento ya se ocupa de los espacios de sobra del cuarto alumno, así que no hace falta strip().
  • Los slices funcionan igual en listas que en strings. Con "maría josé núñez torre":
ExpresiónTipoValor
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.'
resultadostr'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.
  • codigo desempaqueta los dos apellidos con ap1, ap2 = …[-2:] y toma sus tres primeras letras con [:3]. Como un slice nunca da error, un apellido de solo dos letras, como Li, saldría completo (LI) sin romper nada.
  • str(n).zfill(2) convierte 3 en "03". Con una f-string también valdría f"{n:02d}".
  • contar_vocales pasa el texto a minúsculas una sola vez, antes del bucle, y pregunta con in si cada letra está en VOCALES. Con un string, in mira 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 %:

El parte de asistencia, dibujadoUna cuadrícula con cinco alumnos y cuatro sesiones (15, 22, 29 y 13). Ana viene a 3 de 4 (75 %), Luis a 2 (50 %), Marta y María José a las 4 (100 %) y Pablo solo a la última (25 %). Luis y Pablo, por debajo del 75 %, van en coral.El parte de asistencia, dibujado15/0922/0929/0913/10AnaXX·X3/4 75%LuisX·X·2/4 50%MartaXXXX4/4100%María JoséXXXX4/4100%Pablo···X1/4 25%75 %
Cada fila es un conjunto de fechas; el porcentaje es su tamaño entre las 4 sesiones, y por debajo del 75 % sale el aviso.

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 las fechas (que es una lista ordenada) y no el conjunto vino (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 como 50% en 5 caracteres. Ana, 3 / 4 = 0,75, justo el mínimo: como la condición es pct >= 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é. sorted compara carácter a carácter según el código Unicode: las dos empiezan por «Mar», y luego la t va 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:

  1. Después de la línea 2, partes se dibuja como una lista de tres casillas. La del medio está vacía: es el string "" que aparece entre los dos espacios.
  2. Después de la línea 4, bien tiene solo dos casillas: split() se ha comido los dos espacios como si fueran uno.
  3. En el bucle, fíjate en cómo p va tomando "Ana" y luego "García", y cómo iniciales pasa de "" a "A" y a "AG": cada += crea un string nuevo.
  4. Cambia bien por partes en el for y vuelve a ejecutar: verás exactamente en qué paso salta el IndexError (cuando p es 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]:** de a a b sin incluir b. 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 de float.
  • Formato: f"{x:<10}", f"{x:>8.2f}", f"{n:03d}", f"{p:.0%}".

Crear, medir y recorrer

Quiero…CódigoResultado
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
Longitudlen("Núñez")5
Primer / último carácters[0], s[-1]Un string de 1 carácter
Un trozo"03/09/2007"[3:5]'09'
Los últimos 4fecha[-4:]'2007'
Al revéss[::-1]String invertido
Recorrer letra a letrafor letra in s:Un carácter por vuelta
Unir"Ana" + " " + "Gil"'Ana Gil'
Repetir"-" * 2020 guiones
Número a stringstr(7.5)'7.5'

Transformar, buscar y comprobar

Quiero…CódigoResultado
Quitar espacios de los extremos" Ana ".strip()'Ana'
Un solo espacio entre palabras" ".join(s.split())Espacios normalizados
Mayúsculas / minúsculass.upper(), s.lower()
Mayúscula en cada palabra"ana gil".title()'Ana Gil'
Comparar sin mayúsculasa.lower() == b.lower()True / False
Cambiar trozos"7,5".replace(",", ".")'7.5'
¿Está?"@" in correoTrue / False
¿Dónde? (puede no estar)correo.find("@")Posición o -1
¿Dónde? (tiene que estar)correo.index("@")Posición o ValueError
Contars.count(".")Entero
Empieza / terminas.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ódigoResultado
Meter un valorf"Nota: {nota}"Nota: 7.456
2 decimalesf"{nota:.2f}"7.46
Texto a la izquierda en 10f"[{'Ana':<10}]"[Ana ]
Número a la derecha en 6f"[{7.5:6.2f}]"[ 7.50]
Centrado con rellenof"{'FP1':=^9}"===FP1===
Entero con cerosf"{7:03d}"007
Porcentajef"{0.75:.0%}"75%
Una llave literalf"{{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íntomaCausa 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 assignmentIntentas 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 foundindex no encuentra el trozo: usa find o comprueba con in
Un trozo vacío '' en la lista de splitSeparador repetido o al principio/final: usa split() o filtra
1e+01 en lugar de 10.00Formato .2 sin f: usa .2f
La tabla se descolocaAnchos distintos en cabecera y filas, o un valor más largo que su ancho
"Ana" == "ana" da FalseMayú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

  1. ¿Guardo en una variable todo lo que devuelven los métodos de string?
  2. ¿Separo las palabras con split() sin argumento y limpio cada trozo con strip()?
  3. ¿Compruebo la longitud (o que el string no esté vacío) antes de usar índices?
  4. ¿Mis f-strings usan los mismos anchos en la cabecera y en las filas, y .2f (con la f) 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 →

Sigue aprendiendo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *