Cadenas, números y f-strings

Las f-strings son lo que más se escribe en Python, y tienen bastante más dentro de lo que parece. Esta lección va de eso, de por qué las cadenas no se modifican, y de dos sorpresas con los números: los enteros no tienen límite y round(2.5) da 2.

f-strings: la forma de componer texto

nombre, precio, n = "mochila", 34.5, 7
print(f"{nombre}: {precio:.2f} € x {n} = {precio*n:.2f} €")
print(f"alineado: |{nombre:<12}|{nombre:>12}|{nombre:^12}|")
print(f"miles: {1234567.891:,.2f}   porcentaje: {0.1873:.1%}")
print(f"depurar con =: {precio=}  {n=}")
print(f"binario {10:b}  hex {255:x}  relleno {7:03d}")
salidamochila: 34.50 € x 7 = 241.50 €
alineado: |mochila     |     mochila|  mochila   |
miles: 1,234,567.89   porcentaje: 18.7%
depurar con =: precio=34.5  n=7
binario 1010  hex ff  relleno 007

Dentro de las llaves va cualquier expresión, no solo una variable. Y lo que viene después de los dos puntos es el formato:

Quieres… Escribes
Dos decimales {x:.2f}
Separador de miles {x:,.2f}
Porcentaje {x:.1%}
Ancho fijo, a la izquierda {s:<12}
Centrado {s:^12}
Rellenar con ceros {n:03d}
Las comillas puestas {s!r}
El = dentro de las llaves es para depurar. f"{precio=}" imprime precio=34.5: el nombre y el valor, sin escribirlo dos veces. Sustituye a la mitad de los print que uno pone mientras busca un fallo.

Las cadenas no se modifican

s = "  Hola Mundo  "
s.strip()
s.replace("Mundo", "Python")
salidaoriginal  '  Hola Mundo  '
strip     'Hola Mundo'
lower     'hola mundo'
replace   '  Hola Python  '
y s sigue siendo '  Hola Mundo  '
Ese «y s sigue siendo» es el error más repetido de quien empieza. Ningún método de str modifica la cadena: todos devuelven una nueva. Hay que recoger el resultado: s = s.strip(). Lo mismo pasa con las tuplas y, en general, con todo lo inmutable.

Los métodos que se usan a diario

salidasplit      ['uno', 'dos', 'tres']
join       uno | dos | tres
startswith True   in: True
find       4
partition  ('uno', ',', 'dos,tres')
title      Hola Mundo

join se lee al revés de lo que uno espera: es el separador quien junta la lista, " | ".join(partes), no la lista quien se junta con el separador.

Y para saber si un texto está dentro de otro, in antes que find: if "dos" in t se lee mejor que if t.find("dos") != -1.

Números

7 / 2      # 3.5   siempre float
7 // 2     # 3     división entera
7 % 2      # 1     resto
2 ** 100
salida7 / 2  = 3.5      (siempre float)
7 // 2 = 3        división entera
7 % 2  = 1        resto
2 ** 100 = 1267650600228229401496703205376
los enteros no tienen límite: 302 dígitos en 2**1000

Dos cosas que distinguen a Python. La primera: / siempre devuelve un float, aunque los dos operandos sean enteros; para la división entera está //. La segunda: los enteros no tienen tamaño máximo. No hay desbordamiento, no hay long, no hay nada que elegir; crecen hasta donde quepan en memoria.

Y los decimales, como en todas partes

salida0.1 + 0.2 = 0.30000000000000004
con Decimal = 0.3
round(2.5) = 2   round(3.5) = 4   <- al par más cercano

Lo primero no es un fallo de Python: float es coma flotante binaria y 0,1 no se puede representar exacto. Para dinero, decimal.Decimal, construido desde una cadena: Decimal("0.1"); si le pasas un float ya arrastras el error.

Y ese round(2.5) = 2 no es una errata. Python redondea «al par más cercano» —la regla del banquero— en lugar de «siempre hacia arriba». Con muchos números es más justo, porque no sesga la suma; pero sorprende la primera vez. Si necesitas la otra regla, Decimal deja elegir el modo.

Convertir

int("42")      # 42
float("3.14")  # 3.14
str(42)        # "42"
int("doce")
salidaValueError: invalid literal for int() with base 10: 'doce'

Las conversiones son explícitas y estrictas, acorde con lo que vimos en la lección 2. int() sí tolera los espacios alrededor —int(" 8 ") funciona—, pero nada más.

Para… Usa
Componer texto una f-string
Quitar espacios s.strip(), y recoger el resultado
Partir y juntar s.split(",") y ",".join(partes)
Saber si contiene algo "x" in s
División entera //, no /
Dinero Decimal("0.10")
Texto a número int(s) dentro de un try

Todo el código de esta lección se ejecutó con Python 3.14 en un contenedor limpio antes de publicarla; las salidas y los errores están copiados de esa ejecución.