Las f-strings son una forma moderna y muy conveniente de incrustar valores de variables y expresiones directamente en el texto de una cadena. Para crear una f-string, simplemente se coloca una f antes de la comilla de apertura. Dentro de la cadena, puedes insertar variables envolviéndolas en llaves {}, y Python reemplazará automáticamente {variable} con su valor.
<em># Creamos dos variables con valores numéricos.</em>
uno = 1
dos = 2
<em># Insertamos los valores de 'uno' y 'dos' en la cadena.</em>
f"{uno}, {dos}"
<em># Salida: '1, 2'</em>
El poder de las f-strings reside en su capacidad para contener no solo variables, sino casi cualquier expresión válida de Python dentro de las llaves. Esto permite realizar cálculos sobre la marcha sin necesidad de crear variables intermedias innecesarias.
<em># Realizamos una suma directamente dentro de la f-string.</em>
f"{uno} + {dos} = {uno + dos}"
<em># Salida: '1 + 2 = 3'</em>
Puedes acceder fácilmente a elementos de estructuras de datos como diccionarios (por clave) y listas (por índice) desde una f-string. Esto también se extiende al acceso a atributos de objetos (usando la notación de punto) e incluso a la llamada de funciones.
<em># Accediendo al valor de un diccionario por su clave.</em>
colores = {"rojo": "#ff0000", "verde": "#00ff00"}
f"El color rojo es {colores['rojo']}"
<em># Salida: 'El color rojo es #ff0000'</em>
<em># Accediendo a elementos de una lista por su índice.</em>
datos = [4, 8, 15, 16, 23, 42]
f"Los mejores números: {datos[4]} y {datos[5]}"
<em># Salida: 'Los mejores números: 23 y 42'</em>
<em># Accediendo a atributos de un objeto y llamando a funciones.</em>
from dataclasses import dataclass
@dataclass
class Punto:
x: int
y: int
pos = Punto(23, 42)
f"Posición: {pos.x}, {pos.y}. El valor mayor es {max(pos.x, pos.y)}."
<em># Salida: 'Posición: 23, 42. El valor mayor es 42.'</em>
Usando f-strings para depuración
Desde Python 3.8, las f-strings incluyen una potente herramienta para la depuración rápida. Al añadir un signo de igual (=) después de una variable o expresión dentro de las llaves, le indicas a Python que muestre tanto la expresión en sí como su valor evaluado. Esto es increíblemente útil para inspeccionar código rápidamente.
from datetime import date, timedelta
usuario = "eric_idle"
miembro_desde = date(1975, 7, 31)
delta = date(2022, 4, 11) - miembro_desde
<em># Usa f"{usuario=}" para obtener automáticamente "usuario='eric_idle'".</em>
f"{usuario=} {miembro_desde=}"
<em># Salida: "usuario='eric_idle' miembro_desde=datetime.date(1975, 7, 31)"</em>
Este especificador de depuración se puede combinar con especificadores de formato regulares. También puedes usarlo en expresiones completas para mostrar claramente tanto el cálculo como su resultado.
<em># = muestra el nombre de la variable, : inicia el formato, ,d formatea como un entero separado por comas.</em>
f"{delta.days=:,d}"
<em># Salida: 'delta.days=17,056'</em>
<em># Muestra tanto la entrada como el resultado de un cálculo.</em>
from math import cos, radians
theta = 30
f"{theta=} {cos(radians(theta))=:.3f}"
<em># Salida: 'theta=30 cos(radians(theta))=0.866'</em>
Imprimiendo representaciones de depuración (repr)
Los objetos en Python tienen dos representaciones de cadena: str() (para una salida amigable para el usuario) y repr() (para una salida inequívoca y amigable para el desarrollador). Por defecto, las f-strings usan str(). Para obtener la salida de repr(), puedes usar el indicador de conversión !r, que es un atajo conveniente para llamar a repr() manualmente. El indicador !s llama explícitamente a str().
class Dato:
def __repr__(self): return '<Dato ...>' <em># Representación de depuración</em>
def __str__(self): return 'representación de cadena' <em># Representación de usuario</em>
dato = Dato()
<em># El comportamiento por defecto llama a str(dato)</em>
f"dato: {dato}"
<em># Salida: 'dato: representación de cadena'</em>
<em># Usando el indicador !r se llama a repr(dato)</em>
f"dato: {dato!r}"
<em># Salida: 'dato: <Dato ...>'</em>
Relleno, alineación y truncamiento
Las f-strings proporcionan un control total sobre el formato de salida, incluyendo el ancho del campo, la alineación y los caracteres de relleno. El especificador de formato se coloca después de dos puntos (:).
>alinea el texto a la derecha.<alinea el texto a la izquierda.^centra el texto.- Un número después del carácter de alineación especifica el ancho total del campo.
- Un carácter antes del carácter de alineación especifica el carácter de relleno.
val = "test"
<em># Alinear a la derecha en un campo de 10 caracteres</em>
f"{val:>10}"
<em># Salida: ' test'</em>
<em># Alinear a la izquierda y rellenar el espacio vacío con '_'</em>
f"{val:_<10}"
<em># Salida: 'test______'</em>
<em># Centrar el texto</em>
f"{val:^10}"
<em># Salida: ' test '</em>
Para truncar una cadena a una longitud máxima, se utiliza la sintaxis :.N, donde N es el número de caracteres.
instrumento = "xylophone"
<em># Truncar la cadena a 5 caracteres.</em>
f"{instrumento:.5}"
<em># Salida: 'xylop'</em>
Representaciones numéricas
Existen numerosas opciones de formato para los números. Puedes convertir fácilmente números a binario (b), octal (o) o hexadecimal (x/X). Usar un # añade el prefijo apropiado (0b, 0o, 0x).
respuesta = 42
<em># Representación hexadecimal</em>
f"{respuesta:x}, {respuesta:X}"
<em># Salida: '2a, 2A'</em>
<em># Con un prefijo</em>
f"{respuesta:#x}"
<em># Salida: '0x2a'</em>
<em># Notación científica</em>
f"{respuesta ** 8:e}"
<em># Salida: '9.682652e+12'</em>
El relleno de números con ceros a la izquierda se hace usando 0 como carácter de relleno. Para los flotantes, puedes controlar tanto el ancho total como el número de decimales. También puedes controlar la visualización del signo y añadir separadores de miles.
<em># Rellenar hasta 4 dígitos con ceros a la izquierda</em>
f"{respuesta:04d}"
<em># Salida: '0042'</em>
<em># Para Pi: ancho total 6, 2 decimales, rellenar con ceros</em>
import math
f"{math.pi:06.2f}"
<em># Salida: '003.14'</em>
<em># Mostrar siempre el signo (+ para positivos, - para negativos)</em>
f"{respuesta:+d}"
<em># Salida: '+42'</em>
<em># Usar una coma como separador de miles</em>
num = 1234567890
f"{num:,d}"
<em># Salida: '1,234,567,890'</em>
Temas adicionales y mejores prácticas
Muchos objetos incorporados, como datetime, tienen su propio mini-lenguaje de formato que funciona directamente dentro de las f-strings.
from datetime import datetime
dt = datetime(2022, 4, 11, 13, 37)
<em># Usar códigos de formato de fecha/hora estándar</em>
f"{dt:%Y-%m-%d %H:%M}"
<em># Salida: '2022-04-11 13:37'```</em>
¡Los propios especificadores de formato no tienen por qué ser estáticos! Puedes definirlos dinámicamente usando variables con llaves anidadas.
```python
<em># El ancho y la alineación se definen con variables</em>
valor = "test"
alineacion = "^"
ancho = 10
<em># f'{valor:{alineacion}{ancho}}' se convierte primero en f'{valor:^10}'</em>
f'{valor:{alineacion}{ancho}}'
<em># Salida: ' test '</em>
Si necesitas incluir una llave literal { o } en tu cadena, simplemente duplícala.
f"Llaves literales: {{valor}}"
<em># Salida: 'Llaves literales: {valor}'</em>
Consejos prácticos y posibles problemas
Legibilidad vs. brevedad
Las f-strings permiten incrustar expresiones muy complejas, pero no siempre es una buena idea. Un principio fundamental de Python es la legibilidad del código.
Mala práctica (difícil de leer):
<em># Técnicamente funciona, pero ¿qué está pasando aquí?</em>
print(f"Resultado: {round([user.height for user in users if user.active][0] / 2.54, 2)} pulgadas.")
Buena práctica (separar la lógica de la presentación):
<em># 1. Primero, preparamos los datos</em>
alturas_usuarios_activos = [user.height for user in users if user.active]
altura_primer_usuario_cm = alturas_usuarios_activos[0]
altura_en_pulgadas = round(altura_primer_usuario_cm / 2.54, 2)
<em># 2. Luego, formateamos el resultado</em>
print(f"Resultado: {altura_en_pulgadas} pulgadas.")
El código es más largo, pero ahora es comprensible, comprobable y fácil de modificar. Una regla general: si una expresión dentro de {} ocupa más de 20-30 caracteres o contiene una lógica compleja, es mejor extraerla a una variable separada.
F-strings multilínea
Para cadenas largas o para generar texto multilínea (por ejemplo, consultas SQL, plantillas HTML), usa comillas triples. Las expresiones en su interior también pueden ser multilínea.
usuario = "hypo69"
permisos = ['read', 'write', 'execute']
<em># La expresión dentro de la f-string se divide en varias líneas para mayor legibilidad</em>
reporte = f"""
Reporte de Usuario: {usuario.upper()}
----------------------------------
Permisos: {', '.join(p for p in permisos
if p != 'execute')}
"""
print(reporte)
Cuestiones de seguridad (¡Muy importante!)
Las f-strings en sí mismas son seguras. Una expresión como f"Hola, {nombre}" no es una vulnerabilidad; Python simplemente toma la representación de cadena de nombre.
El peligro surge cuando combinas f-strings con funciones que ejecutan código, como eval().
CÓDIGO EXTREMADAMENTE PELIGROSO — NUNCA HAGAS ESTO:
import os
plantilla_usuario = input("Introduce una plantilla: ") <em># El usuario introduce: '{os.system("reboot")}'</em>
eval(f'f"{plantilla_usuario}"')
Si un usuario introduce {os.system("reboot")}, tu servidor se reiniciará. Esto es una vulnerabilidad de Ejecución Remota de Código (RCE). Nunca pases cadenas proporcionadas por el usuario a funciones como eval(), ni siquiera a través de f-strings. Para plantillas definidas por el usuario, utiliza motores de plantillas seguros como Jinja2.
Rendimiento: ¿por qué las f-strings son tan rápidas?
Una f-string no es una llamada a una función; es parte de la sintaxis de Python. El compilador analiza la f-string y la divide en partes literales y expresiones a evaluar. Esto se compila en un bytecode altamente eficiente que construye la cadena final en una sola operación rápida. Es mucho más rápido que el método .format(), que debe analizar la cadena de formato en tiempo de ejecución.
Herramientas de migración
Herramientas como flynt y pyupgrade pueden convertir automáticamente código antiguo que usa .format() y % a las modernas f-strings, lo cual es una excelente manera de modernizar rápidamente un código heredado.