Formateo de cadenas con Template
CategoriasProgramaciónPython

Formateo de cadenas con Template

En el articulo “Formateo de cadenas en Python” vimos las diferentes formas o métodos que existen para formatear cadenas en Python. Las más claras y las más usadas son las f-strings y .format(), aunque como ya vimos, esta última esta en desuso a favor de las f-string. Ahora bien, existe una ultima manera de formatear cadenas que es a través de la clase Template.

Esta clase proviene del módulo string y permite definir plantillas de texto con marcadores de posición que luego serán reemplazados con valores dinámicos. Sí, es igual a usar f-string, con la principal diferencia que sirve para datos que provienen de fuentes externas, como por ejemplo la entrada de datos por parte de un usuario.

Otra diferencia importante respecto a las f-strings y .format(), es que Template ofrece una forma segura de manejar cadenas sin permitir la ejecución involuntaria de código Python. O sea, evita código malicioso.

f-strings y .format() son más rápidos y adecuados para la mayoría de los casos, sí, pero Template se vuelve imprescindible cuando los datos provienen de fuentes externas y queremos evitar riesgos de seguridad.

Veamos algunos ejemplos.

Como acabo de decir, si tenemos las variables definidas en el mismo código (mismo contexto) y solo necesitamos formatear una cadena, intentaremos usar siempre f-string o en su lugar .format():

name = "Oscar"

age = 36

print(f"Hola, mi nombre es {name} y tengo {age} años.")

Pero, si estamos trabajando con datos externos, como un diccionario cargado desde un archivo o una base de datos, entradas de usuarios, etc. En ese caso usaremos Template:

from string import Template

mensaje = Template("Hola, mi nombre es $nombre y tengo $edad años.")

resultado = mensaje.substitute(nombre="Oscar", edad=36)

print(resultado)

La clase Template dispone de algunos métodos importantes como: substitute() y safe_substitute().

Método substitute()

Reemplaza todas las variables por lo que requiere que todas las variables estén definidas. Si falta alguna, lanza un error KeyError.

Por ejemplo, al hacer:

from string import Template

mensaje = Template("Hola, mi nombre es $nombre y tengo $edad años.")

resultado = mensaje.substitute(nombre="Oscar")

print(resultado)

No le estamos pasando edad, por tanto, lanzaría un error de tipo KeyError.

Método safe_substitute()

Reemplaza solo las variables disponibles y deja las faltantes sin modificar.

Útil para evitar errores en valores opcionales o, en otras palabras, no lanza ningún error.

Al hacer:

resultado = mensaje.safe_substitute(nombre="Oscar")
print(resultado)

Mostraría: “Hola, mi nombre es Oscar y tengo $edad años.”

Como vemos, $edad quedó intacto porque no se proporcionó ningún valor.

¿Cuándo es útil este método?
Pues cuando no sabemos si todas las variables estarán disponibles, así evitamos errores en tiempo de ejecución.

Ejemplo Avanzado con Diccionario

Como dije al principio del post, podemos usar un diccionario:

from string import Template

mensaje = Template("Hola, mi nombre es $nombre y tengo $edad años.")

datos = {"nombre": "Óscar", "edad": 36}

resultado = mensaje.substitute(datos)

O bien:

import json
from string import Template

# Simulamos datos recibidos en formato JSON
json_data = '{"nombre": "Oscar", "edad": 36, "pais": "España"}'

datos = json.loads(json_data)  # Convertimos el JSON en un diccionario

# Creamos la plantilla
mensaje = Template("Hola, mi nombre es $nombre, tengo $edad años y vivo en $pais.")

# Sustituimos los valores dinámicos
resultado = mensaje.substitute(datos)

print(resultado)

Cambiar delimitador

Si por lo que fuera, queremos cambiar el delimitador predeterminado, que es el signo de dolar ($), podemos crear una subclase de Template de la siguiente manera:

from string import Template

class MiTemplate(Template):
    delimiter = "%"  # Ahora las variables empiezan con %

plantilla = MiTemplate("Hola, %nombre! Bienvenido a %ciudad.")

resultado = plantilla.substitute(nombre="Oscar", ciudad="Barcelona")

print(resultado)

Como vemos, ahora en lugar de usar ‘$’ para las variables, usamos el delimitador ‘%’.

Seguridad

A diferencia de las f-strings, Template es más seguro ya que evita la ejecución de código malicioso 😈​ por ejemplo, suponiendo que usáramos f-string con el siguiente código, estaríamos permitiendo que un usuario malintencionado ejecutará código Python de forma arbitraria.

entrada_usuario = input("Introduce tu nombre: ")  # Aquí el usuario puede inyectar código malicioso
print(f"Hola, {eval(entrada_usuario)}!")

Si ejecutamos el código en la terminal y escribimos lo normal, o sea, nuestro nombre, por ejemplo “oscar”, la salida sería correcta, pues mostraría:

“Hola, oscar!”

Ahora bien, si en lugar de introducir nuestro nombre, ejecutamos un comando como:

__import__('os').getcwd()

Como veremos en la imagen de abajo, nos mostraría la ruta del directorio actual.

También podemos ejecutar otro comando como:

__import__('os').system('ls')

Dando como resultado una lista de archivos del directorio actual.

Evidentemente estos comandos “no son peligrosos” pero nos sirve a modo de ejemplo. Ahora imagínate uno como…

__import__('os').system('rm -rf /')

¿Qué locura no?

Por si lo desconoces, estaríamos eliminado recursivamente (-r) y forzadamente (-f) todos los archivos y directorios desde la raíz del sistema (/), es decir, podríamos borrar todo el sistema operativo, con lo que supone eso!

Por todo esto, la solución pasa por usar la clase Template:

from string import Template

entrada_usuario = input("Introduce tu nombre: ")  # Entrada del usuario
mensaje = Template("Hola, $nombre! Bienvenido.")

print(mensaje.substitute(nombre=entrada_usuario))

Ahora da igual que el usuario introduzca cualquier código, ya que Template no evalúa las expresiones, es decir, no ejecutará ningún código, solo lo tratará como un texto cualquiera.

Por ejemplo, suponiendo que escribiéramos:

__import__('os').system('ls')

El resultado sería: “Hola, __import__(‘os’).system(‘ls’)! Bienvenido.”

Resumiendo, la clase Template es útil para definir plantillas de texto y formatear cadenas de texto provenientes de fuentes externas aportando seguridad y flexibilidad, cosa que con f-strings no se consigue.

f-strings y .format() son más rápidos y adecuados para la mayoría de los casos, pero Template se vuelve imprescindible cuando los datos provienen de fuentes externas y queremos evitar riesgos de seguridad.

Una vez más y como siempre digo, si has llegado hasta aquí espero que te haya gustado y resultado útil este artículo. Un saludito 😉

Sobre el autor

Comparte:

Este artículo está publicado bajo una licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional . Puedes compartirlo y adaptarlo, incluso con fines comerciales, siempre que cites al autor y mantengas esta misma licencia.

Deja una respuesta

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

Información básica sobre protección de datos
Responsable Óscar Martínez +info...
Finalidad Gestionar y moderar los comentarios +info...
Legitimación Consentimiento del interesado. +info...
Destinatarios No se cederán datos a terceros, salvo obligación legal +info...
Derechos Acceder, rectificar y cancelar los datos, así como otros derechos. +info...
Información adicional Puedes consultar la información adicional y detallada sobre protección de datos en nuestra página de política de privacidad.

Este sitio esta protegido por reCAPTCHA y laPolítica de privacidady losTérminos del servicio de Googlese aplican.

El periodo de verificación de reCAPTCHA ha caducado. Por favor, recarga la página.