Strings en Python — teoría con una lista de clase, índices que empiezan en cero y f-strings que alinean columnas (y el strip() que no limpió nada)
Me tocó pasar a limpio la lista de clase del subgrupo. Venía de un formulario y cada uno había escrito su nombre como le había dado la gana: espacios delante, espacios detrás, mayúsculas donde no tocaban. *»Fácil: strip() para quitar los espacios y title() para las mayúsculas»*. Lo probé con el primer nombre:
# La lista de clase venía de un formulario: espacios de más y mayúsculas al azar
nombre = " gARCÍA pérez, ANA "
nombre.strip() # quitar los espacios de los extremos
nombre.title() # poner en mayúscula la primera letra de cada palabra
print(f"[{nombre}]") # los corchetes dejan ver dónde empieza y dónde acaba
Al ejecutarlo:
[ gARCÍA pérez, ANA ]
Ni un error, y ni un cambio. Los espacios seguían ahí y la g seguía en minúscula. «¿Entonces para qué sirven?» Sirven, pero no como yo creía: strip() y title() no cambian el string que tienen delante. Lo que hacen es devolver un string nuevo ya limpio… y yo no lo estaba guardando en ningún sitio. Python lo creaba y lo tiraba en la misma línea. Los strings en Python son inmutables: una vez creados, no cambian nunca.
Hoy entendemos los strings a fondo: cómo se escriben, qué son las secuencias de escape, cómo se llega a cada carácter con índices y slices, por qué son inmutables, cómo se cambian mayúsculas y minúsculas, cómo se busca dentro de ellos, cómo se trocean y se juntan con **split y join, y cómo se les da formato con f-strings para que las columnas salgan alineadas. El ejemplo que nos acompaña es una lista de clase desordenada**: nombres mal escritos, DNIs, fechas y notas. Si vienes de contenedores asociativos, aquí vas a ver que un string se parece mucho a una lista… hasta que intentas cambiarlo. Cuando acabes, la práctica de strings te espera con tres programas completos.
Tabla de Contenidos
¿Qué es un string?
Un string (tipo str) es una secuencia de caracteres: letras, cifras, espacios, signos… Se escribe entre comillas, y da igual que sean dobles o simples. Lo único que importa es cerrar con las mismas que abriste.
nombre = "Ana García Pérez" # con comillas dobles
grupo = 'GCID-43' # o simples: dan exactamente lo mismo
frase = "La profe dijo: 'mañana hay control'" # unas comillas dentro de las otras
aviso = """Entrega de la práctica:
viernes antes de las 23:59""" # triples: el salto de línea es parte del texto
print(frase)
print(aviso)
print(len(nombre)) # 16: los espacios también son caracteres
print(len("Núñez")) # 5: la ñ y la ú cuentan como un carácter cada una
print(len("")) # el string vacío existe y mide 0
print(type(grupo))
Esto es lo que imprime:
La profe dijo: 'mañana hay control' Entrega de la práctica: viernes antes de las 23:59 16 5 0 <class 'str'>
Vamos por partes:
"Ana García Pérez"y'GCID-43'son exactamente el mismo tipo de dato. Elegir unas comillas u otras sirve para poder meter las otras dentro sin problemas, como enfrase: las comillas simples de dentro no cierran el string, porque se abrió con dobles.- Las triples comillas (
"""o''') permiten escribir un string en varias líneas. El salto de línea que escribes al pulsar Intro forma parte del texto, y por esoavisose imprime en dos líneas. lencuenta caracteres, y los espacios también lo son. Cuéntalo tú: «Ana» (3) + espacio (1) + «García» (6) + espacio (1) + «Pérez» (5) = 16.len("Núñez")da 5. Lañy laúson un carácter cada una, aunque en la tecla parezcan «especiales». Python trabaja con Unicode, así que una letra con tilde es una letra más.""es el string vacío: un string de verdad, que mide 0. Lo vas a ver mucho al trocear textos.
En Python no existe un tipo «carácter» aparte, como el char de Java o de C. Un carácter suelto es, simplemente, un string de longitud 1.
Secuencias de escape
Hay caracteres que no se pueden escribir tal cual dentro de las comillas: un salto de línea, un tabulador, las propias comillas… Para ellos existen las secuencias de escape, que empiezan siempre por la barra invertida \:
| Secuencia | Significa | Ejemplo | Se ve |
|---|---|---|---|
\n | Salto de línea | "Hola\nAdiós" | Hola (y Adiós debajo) |
\t | Tabulador | "Ana\t7.5" | Ana, un hueco y 7.5 |
\' | Comilla simple | 'It\'s' | It’s |
\" | Comilla doble | "Dijo \"sí\"" | Dijo «sí» |
\\ | Una barra invertida | "C:\\" | C:\ |
\xhh | Carácter por su código hexadecimal | "\x40" | @ |
\N{nombre} | Carácter Unicode por su nombre | "\N{BLACK HEART SUIT}" | ♥ |
print("Nombre\tNota") # \t: tabulador
print("Ana\t7.5")
print("Primera línea\nSegunda línea") # \n: salto de línea
print('Dijo \'vale\' y se fue') # \' : comilla simple dentro de comillas simples
print("Barra invertida: \\") # \\ : una barra de verdad
print(len("\n"), len("\\")) # cada secuencia de escape es UN solo carácter
ruta = "C:\notas\tema7" # ¡cuidado! Python ve un \n y un \t
print(ruta)
ruta_bien = r"C:\notas\tema7" # con r delante, string "en bruto": sin escapes
print(ruta_bien)
Salida:
Nombre Nota Ana 7.5 Primera línea Segunda línea Dijo 'vale' y se fue Barra invertida: \ 1 1 C: otas ema7 C:\notas\tema7
Lo que conviene llevarse de aquí:
len("\n")ylen("\\")dan 1. Aunque escribas dos caracteres en el código, la secuencia de escape es un solo carácter dentro del string.- El tabulador salta hasta la siguiente «parada» de la terminal. Sirve para cosas rápidas, pero si quieres columnas de verdad, alineadas pase lo que pase, usa las f-strings del final del artículo.
- La ruta
"C:\notas\tema7"es la trampa clásica de Windows. Python ve\n(salto de línea) y\t(tabulador) donde tú veías carpetas. Por eso sale «C:», un salto de línea, «otas», un tabulador y «ema7». - Con una **
rdelante** de las comillas (raw string, string «en bruto»), Python no interpreta ninguna barra. Es la forma cómoda de escribir rutas de Windows y, como verás en el tema siguiente, expresiones regulares.
Escribir rutas de Windows con barras simples y sin r. A veces «funciona» por casualidad ("C:\datos" no tiene ninguna secuencia de escape válida, aunque las versiones recientes de Python ya avisan con un SyntaxWarning) y otras se rompe en silencio. Usa r"C:\…" o barras normales /, que Python también entiende en Windows.
Índices: llegar a cada carácter
Un string es una secuencia, igual que una lista: cada carácter ocupa una posición, y la primera es la 0. Con corchetes llegas a cualquiera de ellas. Para practicarlo, usamos un DNI y una fecha de nacimiento, que tienen siempre la misma forma:
dni = "45123456D" fecha = "03/09/2007" # día/mes/año, siempre con dos cifras print(dni[0], dni[8]) # primer y último carácter: posiciones 0 y 8 print(dni[-1]) # -1 es el último, sin tener que contar print(dni[:8]) # del principio hasta la 8, SIN incluirla: las cifras print(fecha[3:5]) # posiciones 3 y 4: el mes print(fecha[-4:]) # los 4 últimos caracteres: el año print(fecha[:2], fecha[3:5], fecha[6:]) # día, mes y año por separado print(dni[::2]) # de dos en dos: posiciones 0, 2, 4, 6 y 8 print(fecha[::-1]) # paso -1: el string al revés print(dni[5:50]) # un slice que se pasa del final NO da error
Y su salida:
4 D D 45123456 09 2007 03 09 2007 4135D 7002/90/30 456D
Fíjate primero en el dibujo, porque es la clave de todo el apartado:
Cada carácter tiene dos índices: el normal, que cuenta desde el principio empezando en 0, y el negativo, que cuenta desde el final empezando en -1. En "03/09/2007" el último carácter es a la vez fecha[9] y fecha[-1]. Los negativos son muy cómodos para «lo último» sin saber cuánto mide el string.
Ahora, los slices (o segmentos). cadena[a:b] devuelve un string nuevo con los caracteres desde la posición a **hasta la b sin incluirla. La forma más fácil de no equivocarse es la del dibujo: imagina que los índices son rayas entre los caracteres**. fecha[3:5] corta en la raya 3 y en la raya 5, y lo que queda en medio es "09", el mes.
Línea a línea:
| Expresión | Qué toma | Resultado |
|---|---|---|
dni[0], dni[8] | Las posiciones 0 y 8 | 4 y D |
dni[-1] | El último carácter | D |
dni[:8] | Desde el principio hasta la 8, sin ella | 45123456 |
fecha[3:5] | Posiciones 3 y 4 | 09 |
fecha[-4:] | Desde la cuarta por el final hasta el final | 2007 |
fecha[:2], fecha[3:5], fecha[6:] | Día, mes y año | 03, 09, 2007 |
dni[::2] | Posiciones 0, 2, 4, 6 y 8 (paso 2) | 4135D |
fecha[::-1] | Todo, al revés (paso -1) | 7002/90/30 |
dni[5:50] | De la 5 hasta «la 50», que no existe | 456D |
Tres detalles que suelen pillar:
- Si omites el primer número, el slice empieza al principio; si omites el segundo, llega hasta el final.
fecha[:2] + fecha[2:]es siempre la fecha entera: el corte en la raya 2 la parte en dos sin perder nada. - Un tercer número es el paso. Con
::2vas de dos en dos, y con::-1recorres el string hacia atrás. Es la forma típica de darle la vuelta a un string. - Un slice que se pasa del final no da error: devuelve lo que haya. Un índice suelto que se pasa, sí:
dni = "45123456D" print(dni[9]) # el DNI tiene 9 caracteres: sus posiciones van de 0 a 8
Al ejecutarlo:
Traceback (most recent call last):
File "error_indice.py", line 2, in <module>
print(dni[9]) # el DNI tiene 9 caracteres: sus posiciones van de 0 a 8
~~~^^^
IndexError: string index out of range
Pensar que len(s) es la última posición. Si el DNI mide 9, sus posiciones van de 0 a 8, y dni[9] no existe. El último carácter es dni[len(dni) - 1], que se escribe mucho más cómodo como dni[-1].
Cuando dudes con un slice, cuenta las rayas, no las casillas. [3:5] son 5 − 3 = 2 caracteres, siempre.
Los strings son inmutables
Aquí está el fallo de mi lista. Una lista se puede cambiar por dentro: notas[0] = 10 sustituye el primer elemento. Un string no. Si lo intentas, Python te para en seco:
nombre = "ana" nombre[0] = "A" # intentar cambiar la primera letra "en su sitio"
Esto es lo que imprime:
Traceback (most recent call last):
File "error_asignar.py", line 2, in <module>
nombre[0] = "A" # intentar cambiar la primera letra "en su sitio"
~~~~~~^^^
TypeError: 'str' object does not support item assignment
«Does not support item assignment»: un string no admite que asignes a una de sus posiciones. Y lo mismo pasa con todos sus métodos: ninguno cambia el string original. strip, title, upper, replace… todos devuelven un string nuevo. Si no lo guardas, se pierde:
nombre = " gARCÍA pérez, ANA "
limpio = nombre.strip() # strip DEVUELVE un string nuevo: hay que guardarlo
bonito = limpio.title() # y title devuelve otro más
print(f"[{nombre}]") # el original sigue intacto
print(f"[{limpio}]")
print(f"[{bonito}]")
nombre = nombre.strip().title() # lo habitual: reasignar la misma variable
print(f"[{nombre}]") # title trabaja sobre lo que devuelve strip
print(nombre[0]) # LEER un carácter sí se puede
Salida:
[ gARCÍA pérez, ANA ] [gARCÍA pérez, ANA] [García Pérez, Ana] [García Pérez, Ana] G
Sigue el dibujo de arriba abajo:
nombre.strip()crea un string nuevo sin los espacios de los extremos. Como esta vez sí lo guardamos enlimpio, no se pierde. El original sigue intacto: la primera línea de la salida lo demuestra.limpio.title()crea otro string, con la primera letra de cada palabra en mayúscula y el resto en minúscula:"García Pérez, Ana".- La línea
nombre = nombre.strip().title()es la forma habitual de trabajar. Se lee de izquierda a derecha:strip()devuelve un string nuevo,title()se aplica sobre ese nuevo y devuelve otro, y la asignación hace quenombreapunte al último. El string del principio no ha cambiado: simplemente, ya nadie lo apunta. nombre[0]funciona sin problemas: leer una posición sí se puede. Lo que no se puede es escribir en ella.
Llamar a un método de string en una línea suelta (nombre.strip()) y esperar que la variable cambie. No da error, así que el fallo pasa desapercibido. Si el método devuelve un string, la línea tiene que tener un = delante.
¿y si de verdad necesito cambiar una letra? Se construye un string nuevo con trozos del viejo: "A" + nombre[1:] cambia la primera letra. Y si vas a hacer muchos cambios carácter a carácter, a veces compensa pasar a lista (list(texto)), cambiar la lista y volver a juntarla con "".join(…).
Mayúsculas y minúsculas
Los métodos de mayúsculas son los que más vas a usar para limpiar datos escritos por personas:
texto = "maría josé o'neill de la fuente"
print(texto.upper()) # todo en mayúsculas (también la í y la é)
print(texto.capitalize()) # solo la primera letra del string
print(texto.title()) # la primera letra de cada palabra
print("it's fp1".title()) # title no sabe de apóstrofos ni de siglas
print("FP1 Es Fácil".swapcase()) # invierte mayúsculas y minúsculas
print(texto.islower(), texto.isupper())
# Comparar sin que importen las mayúsculas: se pasan los dos lados a minúsculas
a = "GARCÍA"
b = "García"
print(a == b) # False: para Python son caracteres distintos
print(a.lower() == b.lower()) # True
print("Straße".lower(), "Straße".casefold()) # casefold va más allá que lower
Y su salida:
MARÍA JOSÉ O'NEILL DE LA FUENTE María josé o'neill de la fuente María José O'Neill De La Fuente It'S Fp1 fp1 eS fÁCIL True False False True straße strasse
| Método | Hace | Ojo con |
|---|---|---|
upper() | Todo a mayúsculas | Funciona con tildes y ñ: "josé".upper() es "JOSÉ" |
lower() | Todo a minúsculas | |
capitalize() | Primera letra del string en mayúscula y el resto en minúscula | Por eso «josé» sale en minúscula en la segunda línea |
title() | Primera letra de cada palabra en mayúscula | Pone «De La» y no entiende de apóstrofos: "it's" → "It'S" |
swapcase() | Invierte mayúsculas y minúsculas | |
isupper(), islower(), istitle() | Preguntan, devuelven True/False | No cambian nada |
casefold() | Como lower(), pero más agresivo | Útil para comparar textos en cualquier idioma |
Dos cosas importantes del programa:
title()considera «palabra» todo lo que va después de algo que no es una letra. Por eso"o'neill"queda bien ("O'Neill"), pero"it's"queda mal ("It'S") y"fp1"sale"Fp1". Y las partículas de los apellidos, «de la», salen en mayúscula:"De La Fuente". En la práctica arreglaremos eso a mano.- Para Python,
"GARCÍA"y"García"son strings distintos: cada letra tiene su propio código y laAno es laa. Para comparar sin que importen las mayúsculas, se pasan los dos lados a minúsculas (o a mayúsculas) antes de comparar.
Si comparas textos de cualquier idioma, casefold() es más seguro que lower(). La ß alemana es el ejemplo de manual: lower() la deja como está y casefold() la convierte en ss, que es como se escribe en mayúsculas.
Categorías de caracteres: ¿qué tiene dentro?
Antes de convertir un string a número, o de dar por bueno un DNI, conviene preguntar qué clase de caracteres contiene. Para eso están los métodos is…, que devuelven True solo si todos los caracteres cumplen la condición (y el string no está vacío):
pruebas = ["45123456", "Ana", "Ana Pérez", "7,5", "GCID43", " ", ""]
for t in pruebas:
# repr() muestra las comillas: así se ven los espacios y el string vacío
print(repr(t), t.isdigit(), t.isalpha(), t.isalnum(), t.isspace())
print("²".isdigit(), "²".isdecimal()) # un superíndice es dígito, pero no decimal
print("nota_final".isidentifier(), "2nota".isidentifier()) # ¿valdría como nombre de variable?
Al ejecutarlo:
'45123456' True False True False 'Ana' False True True False 'Ana Pérez' False False False False '7,5' False False False False 'GCID43' False False True False ' ' False False False True '' False False False False True False True False
Lo mismo, en una tabla para leerlo mejor:
| Texto | isdigit() | isalpha() | isalnum() | isspace() |
|---|---|---|---|---|
'45123456' | True | False | True | False |
'Ana' | False | True | True | False |
'Ana Pérez' | False | False | False | False |
'7,5' | False | False | False | False |
'GCID43' | False | False | True | False |
' ' | False | False | False | True |
'' | False | False | False | False |
"Ana Pérez"no esisalpha()por el espacio: el espacio no es una letra. Si quieres comprobar un nombre completo, tendrás que mirar palabra a palabra."7,5"no esisdigit(), por la coma.isdigit()dice si todo son cifras, no si «parece un número». Por eso no sirve para comprobar decimales ni negativos ("-3"también daFalse).- El string vacío da
Falseen todo. Es un detalle que te protege: un campo vacío del formulario nunca pasará por un número. isalnum()es «letras o cifras»:"GCID43"lo cumple."²"esisdigit()pero noisdecimal().isdecimal()es el más estricto de los tres métodos de cifras (isdecimal,isdigit,isnumeric), y el que deberías usar antes de hacerint(texto).isidentifier()dice si el texto valdría como nombre de variable:"nota_final"sí,"2nota"no, porque empieza por cifra.
Concatenar y repetir
Con strings, + une (concatena) y * repite:
nombre = "Ana"
apellidos = "García Pérez"
completo = nombre + " " + apellidos # + no añade espacios: hay que ponerlos
print(completo)
print("-" * 20) # * repite: una línea separadora
print("ja" * 3)
nota = 7.5
print("Nota: " + str(nota)) # + solo une strings: el número se convierte con str()
print("Nota:", nota) # print con comas acepta números y pone el espacio
firma = "" # acumular en un bucle
for parte in ["Ana", "García", "Pérez"]:
firma += parte[0] # += crea un string nuevo en cada vuelta
print(firma)
Esto es lo que imprime:
Ana García Pérez -------------------- jajaja Nota: 7.5 Nota: 7.5 AGP
+junta los strings tal cual, sin añadir nada en medio. Si quieres un espacio entre el nombre y los apellidos, tienes que sumarlo tú:nombre + " " + apellidos."-" * 20es una forma rápida de dibujar una línea separadora de 20 guiones. Es a la concatenación lo que la multiplicación a la suma.- La última parte acumula en un bucle:
firmaempieza vacía y en cada vuelta se le suma la primera letra de una palabra. Cada+=crea un string nuevo (los strings no cambian, ¿recuerdas?) yfirmapasa a apuntarlo. Vuelta a vuelta:
| Vuelta | parte | parte[0] | firma después |
|---|---|---|---|
| 1 | "Ana" | "A" | "A" |
| 2 | "García" | "G" | "AG" |
| 3 | "Pérez" | "P" | "AGP" |
El error más frecuente de este apartado es sumar un string y un número:
nota = 7.5
print("Nota: " + nota) # un string más un float
Salida:
Traceback (most recent call last):
File "error_concat.py", line 2, in <module>
print("Nota: " + nota) # un string más un float
~~~~~~~~~^~~~~~
TypeError: can only concatenate str (not "float") to str
"Nota: " + nota con nota numérica. + no convierte nada por su cuenta: o conviertes tú con str(nota), o usas print con comas, o (lo mejor) usas una f-string: f"Nota: {nota}".
Buscar dentro de un string
Para buscar trozos (substrings) dentro de un string hay varias herramientas. Las probamos con un correo de la universidad:
correo = "ana.garcia@alu.ulpgc.es"
print("@" in correo) # ¿está? True o False
print(correo.find("@")) # posición de la primera aparición
print(correo.find("#")) # -1 si no está, sin error
print(correo.find("a"), correo.rfind("a")) # la primera "a" y la última
print(correo.count(".")) # cuántas veces aparece
print(correo.startswith("ana"), correo.endswith("ulpgc.es"))
usuario, arroba, dominio = correo.partition("@") # 3 trozos, cortando por la primera "@"
print(usuario, "|", arroba, "|", dominio)
print(correo[:correo.find("@")]) # lo mismo que usuario, con find y un slice
print(correo.index("@")) # como find... mientras lo encuentre
Y su salida:
True 10 -1 0 11 3 True True ana.garcia | @ | alu.ulpgc.es ana.garcia 10
Comprobemos los números contando a mano en "ana.garcia@alu.ulpgc.es": a(0) n(1) a(2) .(3) g(4) a(5) r(6) c(7) i(8) a(9) @(10) a(11)…
"@" in correoresponde solo sí o no. Si no necesitas la posición, es lo más claro.find("@")devuelve la posición de la primera aparición: la 10. Si no lo encuentra, devuelve -1, sin error.find("a")encuentra la primeraa, en la posición 0, yrfind("a")(la r es de right, desde la derecha) la última, en la 11.count(".")cuenta las apariciones: hay 3 puntos (uno en el usuario y dos en el dominio).startswithyendswithcomprueban el principio y el final. Son perfectos para validar: «¿es un correo de alumno de la ULPGC?».partition("@")corta por la primera@y devuelve una tupla de tres: lo de antes, el separador y lo de después. Siempre devuelve tres trozos, así que se puede desempaquetar sin miedo.correo[:correo.find("@")]hace lo mismo que elusuariodepartition, combinandofindy un slice: desde el principio hasta la posición de la@, sin incluirla.
index hace lo mismo que find… hasta que no encuentra lo que busca:
correo = "ana.garcia@alu.ulpgc.es"
print(correo.index("#")) # index no devuelve -1: si no lo encuentra, error
Al ejecutarlo:
Traceback (most recent call last):
File "error_index.py", line 2, in <module>
print(correo.index("#")) # index no devuelve -1: si no lo encuentra, error
~~~~~~~~~~~~^^^^^
ValueError: substring not found
¿find, index o in?
| Situación | Usa | Por qué |
|---|---|---|
| Solo quieres saber si está | "@" in correo | Devuelve True/False y se lee solo |
| Necesitas la posición y puede no estar | find | Devuelve -1, y tú decides qué hacer |
| Necesitas la posición y tiene que estar | index | Si no está, el ValueError te avisa de un fallo de verdad |
| Quieres partir en «antes» y «después» | partition | Tres trozos de una vez, sin calcular posiciones |
Usar el resultado de find sin comprobarlo. Si devuelve -1 y haces correo[:-1], no sale ningún error: sale el correo entero sin el último carácter. Antes de usar la posición, comprueba que no es -1.
Trocear y juntar: split, join, replace y strip
Este es el apartado que más vas a usar en los ejercicios de la asignatura: los datos llegan como una línea de texto y hay que convertirla en trozos útiles. Mira esta línea, copiada de una hoja de cálculo con espacios por todas partes:
linea = " García Pérez, Ana ;45123456D; 03/09/2007 ; 7,5 "
partes = linea.split(";") # corta por cada ";" y devuelve una LISTA
print(partes)
limpias = [p.strip() for p in partes] # strip a cada trozo
print(limpias)
apellidos, nombre = limpias[0].split(", ") # "García Pérez" y "Ana"
nota = float(limpias[3].replace(",", ".")) # float no entiende la coma decimal
print(nombre, apellidos, nota)
print("Ana García Pérez".split()) # sin argumento: corta por cualquier hueco
print("Ana García".split(" ")) # con " ": cada espacio es un corte
print("03/09/2007".split("/", 1)) # como mucho 1 corte
print(" ".join(["Ana", "García", "Pérez"])) # el separador es el string de delante del punto
print("/".join(["03", "09", "2007"]))
print("-".join("FP1")) # un string también es una secuencia
print(linea.replace(";", " | ").strip()) # replace cambia TODAS las apariciones
Esto es lo que imprime:
[' García Pérez, Ana ', '45123456D', ' 03/09/2007 ', ' 7,5 '] ['García Pérez, Ana', '45123456D', '03/09/2007', '7,5'] Ana García Pérez 7.5 ['Ana', 'García', 'Pérez'] ['Ana', '', 'García'] ['03', '09/2007'] Ana García Pérez 03/09/2007 F-P-1 García Pérez, Ana | 45123456D | 03/09/2007 | 7,5
Paso a paso:
linea.split(";")corta por cada;y devuelve una lista de strings. El;desaparece, pero los espacios que había alrededor se quedan pegados a los trozos:' García Pérez, Ana ',' 03/09/2007 '…- Por eso el siguiente paso casi siempre es **
strip()a cada trozo**. La list comprehension lo hace en una línea:[p.strip() for p in partes]. limpias[0].split(", ")parte el primer campo por la coma seguida de espacio, y como da exactamente dos trozos, se desempaquetan enapellidosynombre.float("7,5")fallaría, porque Python solo entiende el punto decimal.replace(",", ".")devuelve"7.5"y ese sí se convierte.
Luego vienen las tres formas de usar split, que no son lo mismo:
| Llamada | Corta por | Con "Ana García" (dos espacios) |
|---|---|---|
split() | Cualquier hueco (espacios, tabuladores, saltos de línea), por grande que sea | ['Ana', 'García'] |
split(" ") | Cada espacio, uno a uno | ['Ana', '', 'García'] ← trozo vacío |
split("/", 1) | Por /, pero como mucho 1 vez | ['03', '09/2007'] |
Usar split(" ") para separar palabras. Con dos espacios seguidos aparece un string vacío en medio, y en cuanto hagas trozo[0] sobre él salta un IndexError. Para palabras, split() sin argumento.
join es la operación contraria a split: recibe una secuencia de strings y los une, poniendo entre ellos el string sobre el que se llama. Se escribe «al revés» de como uno espera: el separador va delante del punto.
" ".join(["Ana", "García", "Pérez"])→"Ana García Pérez"."/".join(["03", "09", "2007"])vuelve a montar la fecha."-".join("FP1")→"F-P-1": un string también es una secuencia (de caracteres), así quejoinpone el guion entre cada uno.
" ".join(texto.split()) es la forma más corta de normalizar espacios: quita los de los extremos y deja uno solo entre palabras. split() se come todos los huecos y join vuelve a pegar las palabras con un único espacio. La usaremos en la práctica.
Por último, replace(viejo, nuevo) devuelve una copia con todas las apariciones cambiadas (si solo quieres cambiar las primeras, admite un tercer argumento con cuántas).
strip y sus primos
strip() quita los espaciadores de los dos extremos; lstrip(), solo los de la izquierda (left), y rstrip(), solo los de la derecha (right). Nunca toca los espacios del medio. Y si le pasas un argumento, quita esos caracteres en lugar de espacios:
codigo = " FP1 "
print(f"[{codigo.strip()}] [{codigo.lstrip()}] [{codigo.rstrip()}]")
print("--==FP1==--".strip("-=")) # con argumento: quita esos caracteres de los extremos
nombre = "Ana"
print(f"[{nombre.ljust(10)}]") # a la izquierda en 10 caracteres
print(f"[{nombre.rjust(10, '.')}]") # a la derecha, rellenando con puntos
print(f"[{nombre.center(11, '*')}]") # centrado
print("7".zfill(3), "43".zfill(3)) # ceros a la izquierda hasta 3 cifras
Salida:
[FP1] [FP1 ] [ FP1] FP1 [Ana ] [.......Ana] [****Ana****] 007 043
La segunda mitad del programa hace lo contrario: rellena hasta un ancho. ljust(10) deja "Ana" a la izquierda en 10 caracteres (3 de texto y 7 espacios), rjust(10, '.') lo pone a la derecha rellenando con puntos, y center(11, '*') reparte el hueco: 11 − 3 = 8 asteriscos, 4 a cada lado. zfill(3) añade ceros a la izquierda hasta llegar a 3 cifras: muy útil para números de lista (007) o códigos.
Formatear con f-strings
Desde Python 3.6, la forma preferida de construir textos con datos dentro son las f-strings: un string con una f delante en el que puedes meter cualquier expresión entre llaves. Python la evalúa, la convierte a texto y la coloca en su sitio.
nombre = "Ana García"
nota = 7.456
num = 7
print(f"{nombre} ha sacado un {nota}") # entre llaves va cualquier expresión
print(f"Con un punto más: {nota + 1}")
print(f"Con 2 decimales: {nota:.2f}") # .2f: 2 decimales fijos
print(f"Con 2 cifras: {nota:.2}") # ¡ojo! sin la f son 2 cifras significativas
print(f"[{nombre:15}] [{nota:8.2f}]") # ancho mínimo: texto a la izquierda, números a la derecha
print(f"[{nombre:>15}] [{nombre:^15}]") # > derecha, ^ centro (< izquierda)
print(f"[{nombre:.^16}]") # relleno con puntos y centrado
print(f"N.º de lista: {num:03d}") # ceros a la izquierda hasta 3 cifras
print(f"{num:+d} {-num:+d}") # el signo siempre, también el +
print(f"{255:x} {255:X} {8:o} {1234.5:e}") # hexadecimal, octal y notación científica
print(f"{0.856:.1%}") # porcentaje con 1 decimal
print(f"Las llaves se doblan: {{nota}}")
print("{} tiene un {:.1f}".format(nombre, nota)) # el estilo antiguo, con format()
Y su salida:
Ana García ha sacado un 7.456
Con un punto más: 8.456
Con 2 decimales: 7.46
Con 2 cifras: 7.5
[Ana García ] [ 7.46]
[ Ana García] [ Ana García ]
[...Ana García...]
N.º de lista: 007
+7 -7
ff FF 10 1.234500e+03
85.6%
Las llaves se doblan: {nota}
Ana García tiene un 7.5
Lo básico está en las dos primeras líneas: {nombre} y {nota} se sustituyen por su valor, y {nota + 1} demuestra que entre llaves puede ir una expresión entera, no solo una variable (7.456 + 1 = 8.456).
Lo potente viene después de los dos puntos: el modificador de formato, que no cambia el valor, sino cómo se escribe. Fíjate en las partes de uno completo:
| Modificador | Significa | Ejemplo | Resultado |
|---|---|---|---|
:15 | Ancho mínimo de 15 | f"[{nombre:15}]" | [Ana García ] |
:<15, :>15, :^15 | Alinear a la izquierda, derecha o centro | f"[{nombre:>15}]" | [ Ana García] |
:.^16 | Rellenar con un carácter (aquí, puntos) | f"[{nombre:.^16}]" | [...Ana García...] |
:.2f | 2 decimales fijos | f"{7.456:.2f}" | 7.46 |
:8.2f | Ancho 8 y 2 decimales | f"[{7.456:8.2f}]" | [ 7.46] |
:.2 | 2 cifras significativas | f"{7.456:.2}" | 7.5 |
:03d | Entero con ceros a la izquierda hasta 3 | f"{7:03d}" | 007 |
:+d | Mostrar siempre el signo | f"{7:+d}" | +7 |
:x, :X, :o | Hexadecimal, en mayúsculas, octal | f"{255:x}" | ff |
:e | Notación científica | f"{1234.5:e}" | 1.234500e+03 |
:.1% | Porcentaje con 1 decimal | f"{0.856:.1%}" | 85.6% |
Comprueba las cuentas de la salida:
[Ana García ]: «Ana García» mide 10, y el ancho 15 añade 5 espacios. Un texto se alinea a la izquierda por defecto.[ 7.46]: «7.46» mide 4, y el ancho 8 añade 4 espacios a la izquierda. Un número se alinea a la derecha por defecto, que es justo lo que quieres en una columna de notas.[ Ana García ]: centrado en 15 sobran 5 espacios, que no se pueden repartir a partes iguales; Python pone 2 a la izquierda y 3 a la derecha.0.856como porcentaje es 85.6 %: el formato%multiplica por 100 y añade el símbolo.- Para escribir una llave de verdad dentro de una f-string, se dobla:
{{nota}}se imprime como{nota}. - La última línea es el método
format(), el estilo anterior a las f-strings: las llaves vacías se rellenan, en orden, con los argumentos. Lo verás en código antiguo y en los apuntes; para código nuevo, f-strings.
Escribir :.2 queriendo dos decimales. Sin la f, el número indica cifras significativas, no decimales: con 7.456 da 7.5 y con 10.0 da 1e+01. Para decimales, siempre :.2f.
Columnas alineadas
El gran uso del ancho de campo es imprimir tablas que se lean bien. El truco es usar los mismos anchos en la cabecera y en cada fila:
notas = {"Ana García": 7.456, "Luis Romero": 10, "Marta Gil": 4.95}
print(f"{'Alumno':<14}{'Nota':>6}") # la cabecera con los mismos anchos que las filas
print("-" * 20) # 14 + 6 = 20
for alumno, nota in notas.items():
print(f"{alumno:<14}{nota:>6.2f}") # nombre a la izquierda en 14; nota a la derecha en 6
Al ejecutarlo:
Alumno Nota -------------------- Ana García 7.46 Luis Romero 10.00 Marta Gil 4.95
{alumno:<14}reserva 14 caracteres para el nombre, a la izquierda. «Luis Romero» mide 11, así que le siguen 3 espacios; «Marta Gil», 9, le siguen 5. El nombre es distinto en cada fila, pero la columna siguiente empieza siempre en el carácter 15.{nota:>6.2f}escribe la nota con 2 decimales, a la derecha en 6. Las notas quedan alineadas por el punto decimal:7.46,10.00y4.95terminan en la misma columna. Y10(un entero en el diccionario) sale como10.00, porqueflo trata como decimal.- La línea de guiones mide 14 + 6 = 20, el ancho total de la tabla.
Si una columna se te «descoloca», el problema casi siempre es que la cabecera y las filas usan anchos distintos, o que un valor es más largo que su ancho (el ancho es un mínimo: si no cabe, Python no recorta, ocupa lo que necesite).
Míralo paso a paso en Python Tutor
Python Tutor ejecuta el código línea a línea y dibuja cada variable con su valor. Con los strings es muy útil para ver que los métodos no cambian la variable si no hay asignación. Elige Python 3 y pega este programa:
nombre = " ana garcía "
nombre.strip() # devuelve un string nuevo... que nadie guarda
limpio = nombre.strip() # ahora sí lo guardamos
bonito = limpio.title()
partes = bonito.split() # una lista con dos strings
iniciales = partes[0][0] + partes[1][0]
print(f"[{nombre}]")
print(f"[{bonito}]", partes, iniciales)
Salida esperada:
[ ana garcía ] [Ana García] ['Ana', 'García'] AG
Qué observar, paso a paso:
- Después de la línea 2, mira el panel de variables:
nombresigue teniendo los espacios.strip()ha creado un string limpio, pero como nadie lo ha guardado, no aparece en ningún sitio. - En la línea 3 aparece por fin
limpio, sin espacios.nombreno ha cambiado: ahora hay dos variables con dos strings distintos. - En la línea 5,
partesse dibuja como una lista con dos strings dentro. Python Tutor dibuja las listas aparte, con una flecha, porque son objetos que pueden cambiar; los strings los suele escribir directamente al lado de la variable. - En la línea 6,
partes[0][0]se lee en dos pasos:partes[0]es"Ana"y[0]sobre ese string es"A". Lo mismo conpartes[1][0], que da"G". - Al final, la salida confirma lo del paso 1:
nombrese imprime con todos sus espacios.
Resumen
| Concepto | Lo esencial |
|---|---|
| String | Secuencia de caracteres entre comillas (simples, dobles o triples) |
len(s) | Número de caracteres; los espacios cuentan |
| Escapes | \n, \t, \', \\… cuentan como un carácter; r"…" los desactiva |
| Índices | s[0] el primero, s[-1] el último; fuera de rango → IndexError |
| Slices | s[a:b] de a hasta b sin incluirla; s[::-1] al revés; nunca dan error |
| Inmutables | Ningún método cambia el string: devuelven uno nuevo que hay que guardar |
| Mayúsculas | upper, lower, capitalize, title, swapcase; comparar con lower() en los dos lados |
| Categorías | isdigit, isalpha, isalnum, isspace… False con el string vacío |
+ y * | Concatenar y repetir; + solo une strings (usa str() o f-strings) |
| Buscar | in, find (-1), index (error), rfind, count, startswith, endswith, partition |
| Trocear y juntar | split() por huecos, split(sep) por cada separador, sep.join(lista) |
| Limpiar y rellenar | strip, lstrip, rstrip, replace, ljust, rjust, center, zfill |
| f-strings | f"{expr:formato}": ancho, alineación < > ^, .2f, 03d, % |
Tres ideas para llevarte:
- Los strings no cambian nunca. Cada método devuelve uno nuevo: si la línea no tiene un
=, el resultado se pierde. - Los índices son rayas entre caracteres.
s[a:b]corta en la rayaay en lab, mideb − ay nunca incluye la posiciónb. - **Para separar palabras,
split()sin argumento; para columnas, f-strings con el mismo ancho en cabecera y filas.** Con eso tienes resuelta la mitad de los ejercicios de texto de la asignatura.
En la práctica de strings aplicarás todo esto a una lista de clase de verdad: limpiar nombres escritos de cualquier manera y generar usuarios, validar DNIs y fechas, y sacar un acta con las columnas alineadas. Después, los ejercicios te pondrán a prueba. Y en el siguiente tema, las expresiones regulares: buscar patrones (como «8 cifras y una letra») en lugar de textos exactos.
Siguiente paso: prácticaStrings en Python — práctica limpiando una lista de clase, validando DNIs y sacando un acta alineada (y el 7,5 que float() no quiso leer)¿Te ha servido este artículo? Así puedes apoyar SergioLearns →

3 comentarios