El formateo de strings es el proceso de convertir valores en strings e insertarlos en una plantilla de string. El Zen de Python afirma que debería haber «una forma obvia de hacer algo en Python». Pero cuando se trata del formateo de strings, las cosas son un poco... menos zen.
Puede resultar sorprendente descubrir que hay cuatro formas principales de realizar el formateo de strings en Python, cada una para un escenario distinto.
Parte de esto se debe a la larga historia de Python, y parte a consideraciones como la internacionalización o el saneamiento de la entrada.
Empezaremos por las incorporaciones más recientes a la caja de herramientas del formateo de strings y avanzaremos hacia atrás hasta el formateo de strings «de estilo antiguo» o «de estilo printf()».
f-string
Introducidos en Python 3.6, los f-strings (abreviatura de «formatted strings») o la interpolación de strings literal son una forma de evaluar (y formatear) expresiones y strings de manera rápida y eficiente a un tipo str, usando el prefijo f (o F) delante de las comillas (así: f'{object}').
Los f-strings se pueden usar con todos los tipos de comillas: de una sola línea (' o ") y de varias líneas (''' o """).
Cualquier variable, expresión u otro tipo que se coloque dentro de las {} se evalúa primero, después se convierte a str y, por último, se concatena con los str que haya fuera de las llaves.
En este ejemplo, insertamos dos valores de variables en una frase (un str y un float):
>>> name = 'eighth'
>>> value = 1/8
# The f-string, using the two values.
# The .2f format code truncates, so the value displays as 0.12.
>>> f'An {name} is approximately {value:.2f}.'
'An eighth is approximately 0.12.'
Las expresiones evaluadas pueden ser casi cualquier cosa.
Algunas de las (amplias) posibilidades que se pueden evaluar: strs, números, variables, expresiones aritméticas, expresiones condicionales, tipos integrados, slices, funciones, lambdas, comprensiones o cualquier objeto que tenga definido el método __str__ o __repr__.
De lo simple a lo complejo:
Insertar una variable: el uso más sencillo de un f-string es colocar una variable directamente en el string.
# Assigning a variable.
>>> name = "World"
# Inserting that variable.
>>> f'Hello, {name}!'
'Hello, World!'
Expresiones dentro de las {}: dentro de las llaves se puede evaluar cualquier expresión válida de Python.
Fíjate en que usar comillas dobles dentro de un f-string delimitado por comillas simples (o al revés) evita tener que usar secuencias de escape:
# A dictionary of key:value pairs.
>>> waves = {'water': 1, 'light': 3, 'sound': 5}
# Inserting the whole dict.
>>> f'Wave ranks: {waves}'
"Wave ranks: {'water': 1, 'light': 3, 'sound': 5}"
# An expression can be evaluated inline:
>>> f"Tenfold the value of 'light' is {waves['light'] * 10}."
"Tenfold the value of 'light' is 30."
# A method call can also be evaluated inline:
>>> f'{"hello world!".title()} is a classic greeting.'
'Hello World! is a classic greeting.'
# An f-string can be nested inside another f-string:
>>> f"{f'hello world!'.title()} is a classic greeting."
'Hello World! is a classic greeting.'
Formateo de la salida: el mini-lenguaje de especificación de formato se puede usar para controlar la alineación, la precisión numérica y mucho más.
La especificación de formato va después del valor, separada por :.
# Right-align a value to ten characters, and round it to 3 decimal places.
>>> value = 1 / 7
>>> f'One seventh is {value:10.3f}.'
'One seventh is 0.143.'
# A format specification can be set using variables as well.
>>> padding = 10
>>> precision = 3
>>> f'One seventh is {value:{padding}.{precision}f}.'
'One seventh is 0.143.'
Todo junto: variables, expresiones, llamadas a funciones y formateo de la salida:
>>> precision = 3
>>> f"{30e8 * 111_000:6.{precision}e}"
'3.330e+14'
>>> verb = 'meet'
>>> the_end = ['end', 'of', 'transmission']
>>> f'"Have a {"NICE".lower()} day, I will {verb} you after {30e8 * 111_000:6.{precision}e} light-years."{the_end}'
'"Have a nice day, I will meet you after 3.330e+14 light-years."[\'end\', \'of\', \'transmission\']'
# Did you notice the escaped single-quotes in the previous example?
# Using double quotes instead of single quotes for the f-string means the list's single-quoted strings print cleanly.
>>> f"Have a nice day. {the_end}"
"Have a nice day. ['end', 'of', 'transmission']"
Hay dos limitaciones principales que conviene tener en cuenta.
Las expresiones de los f-string no pueden estar vacías.
Además, antes de Python 3.12, no podían contener comentarios.
>>> f"An empty expression will error: {}"
SyntaxError: f-string: empty expression not allowed
>>> word = 'word'
>>> f"""A comment in a triple quoted f-string: {
word # I chose a nice variable
}"""
'A comment in a triple quoted f-string: word'
La interpolación de strings no se puede usar junto con la API de GNU gettext para la internacionalización (I18N) y la localización (L10N), por lo que se recomienda usar la clase string.Template o el método str.format() que se describen más abajo en lugar de un f-string en cualquier escenario de traducción de «envoltura de strings».
Ten en cuenta también que usar expresiones dentro de las llaves {} de un f-string es parecido a usar eval() o exec(), así que no es muy seguro y nunca debería usarse con la entrada del usuario.
str.format()
El método str.format() sustituye los marcadores de posición del string por los valores que se pasan como argumentos a la función.
Los marcadores de posición se identifican con nombres ({price}), números ({0} o índices) o incluso marcadores (posicionales) vacíos {}.
Por ejemplo:
# A named placeholder and a positional placeholder.
>>> 'My text: {placeholder_1} and {}.'.format(12, placeholder_1='named placeholder')
'My text: named placeholder and 12.'
Igual que los f-strings, el str.format() de Python admite toda una serie de especificadores de formato del mini-lenguaje que se pueden usar para alinear texto, truncar números decimales y mucho más.
El patrón completo de un especificador de formato es {[<name>][!<conversion>][:<format_specifier>]}:
<name> puede ser un marcador de posición con nombre, un número o estar vacío.!<conversion> es opcional y debe ser una de estas tres conversiones: !s para str(), !r para repr() o !a para ascii().
De forma predeterminada, se usa str().:<format_specifier> es opcional y controla cómo se muestra el valor.
Puedes encontrar aquí más información sobre las opciones posibles.Ejemplo de conversiones para una letra con diacrítico:
# Fills in the object at index zero, converted to a string.
>>> 'An e with an umlaut: {0!s}'.format('ë')
'An e with an umlaut: ë'
# Fills in the object at index zero, converted to a repr.
>>> 'An e with an umlaut (object representation): {0!r}'.format('ë')
"An e with an umlaut (object representation): 'ë'"
# Fills in the object at index zero, converted to ASCII.
>>> 'An e with an umlaut converted into ASCII: {0!a}'.format('ë')
"An e with an umlaut converted into ASCII: '\xeb'"
# Fills in the object in the first position,
# then fills in the object in the second position formatted as a repr.
>>> 'She said her name is not {} but {!r}.'.format('Chloe', 'Zoë')
"She said her name is not Chloe but 'Zoë'."
Ejemplos de especificadores de formato habituales:
# Integer and binary/hex representations of the same number.
>>> my_num = 42
>>> f"{my_num} in binary is {my_num:b}. In hex, it is {my_num:x}"
"42 in binary is 101010. In hex, it is 2a"
# Alignment: left (<), right (>), and center (^) using up to ten characters total.
>>> f"[{"left":<10}] [{"right":>10}] [{"center":^10}]"
"[left ] [ right] [ center ]"
# Float precision and scientific notation (up to three decimal places).
>>> pi = 3.141592653589793
>>> f"Fixed: {pi:.3} Scientific: {pi:.3e}"
"Fixed: 3.142 Scientific: 3.142e+00"
# Thousands separator and percentage.
>>> balance = 1000
>>> rate = 0.0225
>>> f"Balance: ${balance:,.0f} Interest rate: {rate:.1%}"
"Balance: $1,000 Interest rate: 2.2%"
# Putting it all together:
>>> items = [("Widget", 1250, 9.991), ("Gadget", 37, 24.503), ("Doohickey", 4, 149.002)]
>>> header = f"{"Item":<12} {"Qty":>6} {"Price":>9}"
>>> print(header)
Item Qty Price
>>> for name, qty, price in items:
... print(f"{name:<12} {qty:>6} {price:>9.2f}")
Widget 1250 9.99
Gadget 37 24.50
Doohickey 4 149.00
Puedes ver más ejemplos al final de esta documentación.
%, o formateo al estilo printf()
El uso del operador % para formatear es el método más antiguo de formateo de strings en Python.
Procede del lenguaje C y permite usar argumentos posicionales para construir un str.
Este método ha sido reemplazado tanto por los f-strings como por str.format(), y por eso el apodo del formateo con % es «Old Style».
Todavía se puede encontrar en Python 2 o en código heredado.
Aunque usar este método funciona en Python 3.x, normalmente se evita el formateo con % porque es propenso a errores, es menos eficiente, tiene menos opciones disponibles y cualquier desajuste entre un marcador de posición y su argumento puede lanzar una excepción.
Usar el operador % es parecido a printf(), así que a veces también se le llama formateo printf.
# Assigning a variable.
>>> name = "Anna-conda"
# Building a string using %.
>>> "The snake's name is %s." % name
"The snake's name is Anna-conda."
En el ejemplo anterior, el operador % sustituye el marcador de posición %s por la variable name en tiempo de ejecución.
Si quieres añadir varias variables a un string, debes proporcionar una tupla que contenga un objeto por cada marcador de posición después del %:
# Assigning variables.
>>> name = "Billy the Kid"
>>> fruit = "grapes"
# Building a string using %.
>>> "Surprisingly, %s's favorite snack was %s." %(name, fruit)
"Surprisingly, Billy the Kid's favorite snack was grapes."
string.Template() (que no hay que confundir con los t-strings de Python 3.14) es una clase del módulo string (a diferencia del tipo str integrado) que forma parte de la biblioteca estándar de Python, pero que hay que importar para poder usarla.
Los strings de plantilla admiten sustitución basada en $ y son mucho más sencillos y menos potentes que las otras opciones mencionadas aquí.
Sin embargo, pueden resultar muy útiles para la internacionalización complicada o para sanear entradas externas.
Se considera que string.Template es más seguro para la entrada de usuario no confiable, porque impide evaluar expresiones arbitrarias o acceder a atributos de objetos, lo que mitiga los ataques de inyección de strings de formato.
>>> from string import Template
>>> name = "Anna-Conda"
# Creating a Template with placeholder text.
>>> template_string = Template("The snake called `$snake_name` has escaped!")
# Calling .substitute() to replace the placeholder with a value.
>>> template_string.substitute(snake_name=name)
'The snake called `Anna-Conda` has escaped!'
Puedes encontrar más información sobre string.Template en la documentación de Python.
Con todas estas opciones y mini-lenguajes, ¿cómo decides por cuál optar cuando formateas strings en Python? Aquí tienes unas cuantas pautas rápidas:
Los f-strings son los más recientes y los más fáciles de leer.
Si no necesitas internacionalizar, deberían ser el método preferido para Python 3.6+.
str.format() es versátil, potente y compatible tanto con gnu gettext como con la mayoría de las versiones de Python.
Si lo que necesitas es sencillez, seguridad o mucha internacionalización, puedes usar string.Template() para mitigar riesgos cuando haya que manejar entradas de los usuarios y para envolver strings de traducción.
El operador % se considera generalmente obsoleto para código nuevo, aunque sigue funcionando en Python moderno.
En general, debería usarse solo para mantener compatibilidad con bases de código más antiguas.
El formateo con % es más propenso a errores (y tiene menos funcionalidad) que otros métodos, y puede dar problemas al mostrar caracteres no ASCII.
Consulta los detalles de compatibilidad de tu distribución concreta de Python si tienes intención de usarlo.
Si quieres ir más allá, todo sobre el formato y Python String Formatting Best Practices son buenos puntos de partida.