Formateando texto en Python

¿Qué vamos a ver en este artículo? La idea básica es ver cómo trabajar con cadenas de texto en Python.

  • Formas de formatear cadenas.
  • Problemas a solucionar.
  • La forma antigua.
  • Usando el método format de los objetos str (o la función format).
  • Usando f-strings.
  • Usando el objeto string.Template.
  • Modificar, separar, reemplazar,…

Primero de todo voy a importar la siguiente biblioteca:

¿Por qué no concatenar texto? Porque puede ser más lento, porque puede ser menos mantenible, porque puede ser más lioso, porque puede ser más propenso a errores, porque puede ser menos legible,…

La idea principal del artículo es convenceros de que algo como lo siguiente, concatenar texto, siempre será peor en la mayoría de las ocasiones:

Formas de formatear texto

Tenemos varias formas:

  • Usando la forma antigua (old style o printf-style) mediante el uso del operador % para texto. Es usable en CPython 2 y CPython 3.
  • Usando el método format de un objeto str o la función format. Es compatible con versiones de CPython superiores o igual a 2.6.
  • Usando f-strings. Introducido en la versión de Python 3.6. Pypy lo incluye desde su versión 6.0 (que es compatible con CPython 3.5).
  • Usando el objeto Template del módulo string. Está disponible desde hace más de 15 años tanto en Python 2 como en Python 3 (commit).

Problemas a solucionar

Vamos a formatear y modificar texto de tal forma que sea sencillo crear una plantilla para un correo o para meter partes de un informe de forma sencilla o para actualizar las partes de una nueva factura,… Es decir, esto tendrá sentido cuando tenemos que actualizar partes de un texto y el resto se mantiene sin cambios de forma que la automatización resulte de gran ayuda.

La forma antigua

No te voy a enseñar como usarla. No la uses. En la documentación de las nuevas versiones de Python aparece muy poco para desincentivar su uso,… En algún sitio de la documentación dicen:

The formatting operations described here exhibit a variety of quirks that lead to a number of common errors (such as failing to display tuples and dictionaries correctly). Using the newer formatted string literals, the str.format() interface, or template strings may help avoid these errors. Each of these alternatives provides their own trade-offs and benefits of simplicity, flexibility, and/or extensibility.

que, traducido, sería algo así como:

Las operaciones de formateado mostradas a continuación presentan una serie de peculiaridades que pueden llevar a cometer una serie de errores típicos (como que falle a la hora de mostrar correctamente tuplas y diccionarios). Usando los nuevos strings formateados de forma literal (f-strings), la interfaz str.format() o las plantillas de strings pueden ayudar a evitar este tipo de errores. Cada una de estas alternativas proporciona sus propias contrapartidas y ventajas de simplicidad, flexibilidad y/o extensibilidad.

Pros:

  • Compatible con versiones viejas de Python. Si te ha tocado lidiar con código legado quizás tengas que usarlo. Aunque el siguiente método que voy a comentar, el método format de los objetos str, es compatible con Python 2.6 y 2.7 y podrías usar ese método. Si tu código a mantener es más antiguo que python 2.6 lo siento mucho por ti.

Contras:

  • Es feo.
  • Puede llevar a errores (ver ejemplo más abajo).
  • Tienes que respetar el orden de inserción (*).
  • No puedes formatear fechas, no puedes usar la clave o valor de diccionarios,…
  • Puede confundirse con el operador módulo para números. Aunque esto puede suceder con el operador suma y multiplicación con los strings por lo que este argumento no tiene tanto peso.

Usando el método format

Esta forma existe para mejorar muchos de los problemas que había con la forma antigua. Existe un mini lenguaje que ayuda a representar cierta información que se puede usar tanto con este método, con los f-strings como con la función format, la cual funciona de forma muy similar al método format y por eso la estoy obviando en este tutorial.

La especificación del mini lenguaje de formateo de texto la puedes encontrar aquí. A lo largo de lo que resta de tutorial vamos a hacer uso de este mini lenguaje e intentaré explicar qué es lo que estoy haciendo en cada momento.

Para usar el método format podemos hacer lo siguiente (recupero el primer ejemplo):

Con lo anterior ya podemos ver algunas mejoras con respecto a la forma antigua. texto es más legible. Podríamos reutilizar variables:

Podríamos formatear fechas:

En este ejemplo podéis ver que estoy usando : después de la variable que va entre claves ({...:...}) para especificar una serie de cosas de cómo quiero representar las variables. Ahí es donde entra en acción el mini lenguaje de formateado. En el ejemplo anterior hemos formateado una fecha (ver el comentario con una URL en el código para saber más).

Veamos algunos ejemplo de cómo usarlo:

Lo anterior escribe enteros en notación decimal. Si lo quiero escribir en hexadecimal, por la razón que sea, podría usar:

Lo puedo escribir también con decimales:

En los dos ejemplos anteriores hemos formateado números decimales indicándole que muestre uno o dos decimales mediante el uso de .2f o .1f.

Para ver más sobre esto le podéis echar un ojo a este otro artículo que escribí hace un tiempo donde se entra más en detalle en el funcionamiento del mini lenguaje de los strings.

Usando el método format podemos hacer más cosas que no podemos con la forma antigua. Algunos ejemplos más, este primero ya lo hemos visto más arriba:

También podemos pasar parámetros usando orden:

En el ejemplo anterior uso el orden de los *args pasados al método donde 1 está en la posición ‘0’ y 2 está en la posición ‘1’ y de esa forma los puedo reusar donde quiera, como he hecho con 1.

Se pueden usar atributos de objetos:

Podrías hacer algo similar con diccionarios.

Bueno, todo esto está muy bien y estamos viendo que tenemos mejoras con respecto a la forma antigua. Vamos a dar un paso más allá y a empezar a usar los f-strings que son incluso más potentes.

Usando f-strings

Voy a empezar extendiendo el ejemplo de antes para mostrar una de las potenciales ventajas de los f-strings frente a otros métodos.

¡¡Da un error!! ¿Por qué? Seguimos y lo explicamos a continuación…

Rehago lo anterior usando f-strings:

Vemos varias cosas: el código es más conciso lo que podría ayudar a evitar errores, por tener menos código y que, por tanto, sea más mantenible y, además, podemos ejecutar, por ejemplo, funciones o métodos como estoy haciendo aquí con el método dummy.meses, algo que el método format no me ha permitido como has visto en el ejemplo anterior.

Podríamos evaluar otras cosas. Por ejemplo, con los f-strings podemos evaluar expresiones:

En los ejemplos anteriores uso notación exponencial para que veamos más ejemplos de formateos.

Usando Template

Esta es una forma menos potente pero que te puede resolver problemas concretos de forma sencilla. En este caso, lo que se usa es el símbolo del dólar, $, para poder usar variables dentro del texto.

Si quisiéramos usar el símbolo de dólar como parte del textolo tendríamos que hacer así:

Es decir, usando $$ escribirá el símbolo de dolar.

Y no podríamos hacer mucho más… Entonces, ¿para qué usar usar string.Template si es tan limitado? Debido a esas limitaciones puede ser una buena opción si en el texto que hemos de formatear queremos evitar que se ejecute código. Con otras opciones hemos visto que cierto código puede ser ejecutado. Si nosotros no somos los responsables de meter lo que se puede evaluar en el formateado podríamos tener algún problema de seguridad y, en esos casos, string.Template podría ser una posible opción a usar.

Métodos de un objeto str

Además de poder formatear texto y números de muchas formas, el propio objeto str dispone de muchos métodos que pueden resultar muy útiles para que el texto se muestre como deseamos. Veamos algunos…

Por ejemplo, imaginad que os llega una información que no puede tener espacios de ningún tipo (espacios, saltos de línea,…). Podéis usar el método replace para eliminar estos espacios:

Pero lo anterior tiene el problema de que tenemos que especificar otros tipos de ‘espacios’. Por ejemplo, lo siguiente no me eliminaría los saltos de línea

Con string.whitespace podemos ver lo que son espacios:

Mi problema anterior lo podría resolver chapuceramente de la siguiente forma:

Es decir, concatenando llamadas a replace con cada uno de los posibles espacios (espacios en blanco, saltos de linea, tabulaciones,…)… Pero lo anterior no es mantenible ni bonito. Una posible solución podría ser usando los métodos strip y join. El primero lo que hace es separar el texto usando el delimitador que le indique y el segundo lo que hace es unirme partes de texto con el delimitador que considere. Veamos primero uno y luego otro y luego uniremos sus fortalezas para resolver el problema anterior:

Al método split, si no indico el separador a usar por defecto separa el texto por sus espacios (espacio en blanco, tabulación, salto de línea,…).

En método join lo que hace es unir partes:

Entre cada dos valores intercala la coma que he usado en el string. Unamos el uso de ambos métodos para resolver el problema original, el texto sin ningún tipo de espacios:

También nos podría interesar que, antes de unir el texto, cada palabra empezase por mayúscula para así, después de quitar los espacios, poder leer el resultado de forma más sencilla. Para ello podríamos usar el método title:

Si solo quisíeramos en mayúscula la primera palabra del texto podríamos usar el método capitalize.

Quizá queremos un texto que al guardarlo quede de tal forma que todos los signos de puntuación (‘,’, ‘.’, ‘!’, ‘¿’,…) sean sustituidos por un guión bajo (‘_’). Además, queremos que las mayúsculas se conviertan en minúsculas y que los espacios se conviertan en guiones (‘-‘). Por ejemplo, queremos que el texto:

Se convierta finalmente en:

Es decir, todo el texto en minúsculas, los espacios y saltos de línea convertidos en guiones y los signos de puntuación convertidos en guiones bajos.

Para convertir todo a minúsculas podemos usar el método lower (para convertir todo a mayúscula podemos usar el método upper):

Para transformar los espacios ya hemos visto el ejemplo anterior. Modifico el ejemplo anterior con el nuevo texto y el nuevo separador y encadeno, además, el método lower:

Para cambiar los signos de puntuación voy a hacer uso de dos métodos, maketrans y translate. El primero me crea un mapeo de las cosas que quiero eliminar del texto a las cosas por las que las quiero sustituir. Para indicar los signos de puntuación voy a hacer uso de string.punctuation

Primero hago el mapeo que luego usaré en translate:

Lo anterior dice que las ‘,’ las identifique como ‘_’, los ‘.’ como ‘_’, los ‘?’ como ‘_’,…, a la hora de hacer la transformación. La transformación la hago con translate usando ese mapping:

Vemos que las comas y puntos las ha sustituido por guiones bajos.

Bien, ya tenemos todos los ingredientes. Vamos a cocinar la receta:

¿Qué ha pasado? Que el guión está entre los caracteres de puntuación y me los sustituye también. Podemos hacer varias cosas, o cambiar el orden de alguna operación o ignorar el guión en el mapeo. La segunda me parece más limpia y menos propensa a errores por lo que vamos con ella:

Genial. Lo hemos conseguido.

Podríamos, también, comprobar si nuestro texto empieza o termina por un texto usando los métodos startswith o endswith, respectivamente:

Podemos comprobar si el texto es numérico:

Existen otros métodos interesantes y os invito a que les echéis un ojo:

Resumen y conclusiones

Hemos visto diferentes formas de formatear texto en Python y de trabajar con el mismo. Combinando diferentes metodologías de los que hemos visto y otras que se nos puedan ocurrir nos permitiría hacer cosas muy chulas.

¿Cuándo usar unas formas u otras de formateo?

Yo uso siempre los f-strings si estoy trabajando con una versión de Python >= 3.6. Normalmente no me llega texto de usuarios por lo que me evito el peligro de que ejecuten código potencialmente maligno. Si estás en una versión antigua usa format. Si te llega texto de usuarios prueba con string.Template para evitar problemas de seguridad (o puedes usar sistemas de plantillas como Jinja).

Algunas referencias:

Deja un comentario

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

seven + three =