string.Template: plantillas de texto simples y seguras

Publicado el: 09/09/2026
Tempo de leitura: 6 minutos
Desarrollador creando plantillas de texto con string.Template en Python

string.Template es una forma ligera de crear textos con campos reemplazables en Python. En lugar de mezclar lógica compleja dentro de una cadena, defines marcadores como $nombre y proporcionas sus valores por separado. Este enfoque resulta útil para notificaciones, correos, informes, archivos de configuración y textos editados por personas que no necesitan conocer toda la sintaxis de las f-strings.

Qué es string.Template

La clase Template pertenece al módulo estándar string. Reconoce placeholders que comienzan con el signo de dólar. Un campo puede escribirse como $nombre o ${nombre}. Las llaves son prácticas cuando el identificador está junto a otros caracteres.

from string import Template
plantilla = Template("Hola, $nombre. Tu plan es ${plan}Premium.")
texto = plantilla.substitute(nombre="Ana", plan="Python ")
print(texto)

El texto final se produce sin concatenaciones manuales. Esto reduce errores de puntuación y mantiene el mensaje legible. Para repasar conceptos relacionados, consulta Strings en Python y f-strings en Python.

substitute y safe_substitute

substitute exige que todos los campos utilizados tengan un valor. Si falta una clave, Python genera KeyError. Este comportamiento estricto es útil cuando un dato incompleto debe detener el proceso.

plantilla = Template("Pedido $codigo para $cliente")
plantilla.substitute(codigo="A-10")  # KeyError: cliente

safe_substitute conserva el marcador sin resolver. Es apropiado para vistas previas, editores, borradores y flujos donde la información llega en varias etapas.

borrador = plantilla.safe_substitute(codigo="A-10")
print(borrador)  # Pedido A-10 para $cliente

La palabra “safe” no significa que el resultado sea seguro automáticamente para HTML, SQL o comandos del sistema. Solo evita la excepción causada por una clave ausente. La validación y el escape siguen dependiendo del destino.

Cuándo usar Template en lugar de f-strings

Las f-strings suelen ser mejores cuando el desarrollador controla el código y necesita expresiones, formatos numéricos o llamadas a funciones. Template destaca cuando el texto vive fuera del código, por ejemplo en un archivo, base de datos, CMS o panel administrativo.

Su sintaxis limitada es una ventaja. Quien edita el mensaje no puede insertar expresiones Python arbitrarias. La aplicación controla los datos disponibles y la plantilla controla la presentación. Esta separación facilita revisar, traducir y reutilizar contenidos.

Para plantillas almacenadas en archivos, revisa cómo leer archivos TXT y JSON en Python.

Usar diccionarios como mapping

substitute acepta un mapping como primer argumento. Esto facilita integrar resultados de APIs, formularios y documentos JSON.

datos = {"producto": "Curso de Python", "precio": "99 €"}
plantilla = Template("$producto disponible por $precio")
print(plantilla.substitute(datos))

También puedes combinar un diccionario con argumentos nombrados. Los argumentos nombrados tienen prioridad, lo que permite mantener valores predeterminados y reemplazar solo algunos campos.

predeterminados = {"empresa": "Academify", "canal": "sitio web"}
mensaje = Template("$empresa atiende por $canal")
print(mensaje.substitute(predeterminados, canal="WhatsApp"))

Validar los campos

Las versiones recientes de Python ofrecen is_valid() y get_identifiers(). El primero comprueba si la sintaxis es válida; el segundo devuelve los nombres de los campos encontrados.

plantilla = Template("Hola $nombre, pedido $codigo")
if not plantilla.is_valid():
    raise ValueError("Plantilla inválida")
permitidos = {"nombre", "codigo"}
desconocidos = set(plantilla.get_identifiers()) - permitidos
if desconocidos:
    raise ValueError(f"Campos no permitidos: {desconocidos}")

Esta validación conviene antes de guardar una plantilla editada por un usuario. La aplicación puede rechazar campos desconocidos, mostrar un mensaje claro y comprobar que todas las traducciones utilizan las mismas variables internas.

Cambiar el delimitador

Es posible heredar de Template para usar otro delimitador cuando el signo de dólar ya tiene un significado en el texto.

class PlantillaArroba(Template):
    delimiter = "@"

plantilla = PlantillaArroba("Hola @nombre")
print(plantilla.substitute(nombre="Carlos"))

También se puede personalizar el patrón de reconocimiento, pero conviene evitar una sintaxis innecesariamente compleja. Cuanto más sofisticado sea el lenguaje, mayores serán el mantenimiento y la posibilidad de errores. En la mayoría de proyectos basta con conservar el formato estándar o cambiar solo el delimitador.

Ejemplo de renderizador de correos

Un diseño práctico guarda el cuerpo del mensaje en archivos y mantiene la validación en el código Python. La siguiente función carga una plantilla UTF-8, comprueba su sintaxis, restringe identificadores y genera el resultado.

from pathlib import Path
from string import Template

def renderizar(ruta, datos):
    contenido = Path(ruta).read_text(encoding="utf-8")
    plantilla = Template(contenido)
    if not plantilla.is_valid():
        raise ValueError("La sintaxis no es válida")
    desconocidos = set(plantilla.get_identifiers()) - set(datos)
    if desconocidos:
        raise ValueError(f"Campos no permitidos: {desconocidos}")
    return plantilla.substitute(datos)

Este diseño permite que un equipo de contenido cambie la redacción sin modificar la función. El código conserva la responsabilidad sobre codificación, validación, campos autorizados, registro de errores y envío. Para trabajar con rutas, consulta pathlib en Python.

Consideraciones de seguridad

Template no evalúa expresiones Python, pero no es una capa universal de sanitización. Los valores destinados a HTML deben escaparse. Las consultas SQL deben usar los parámetros del controlador de base de datos. Los comandos del sistema deben recibir listas de argumentos en lugar de cadenas construidas con sustituciones.

Pasa a la plantilla el mapping más pequeño posible. Si un objeto contiene información privada, no expongas todos sus atributos. Construye un diccionario específico con los valores aprobados. Así evitas que una plantilla controlada por terceros solicite información que nunca debía mostrar.

También debes decidir si los placeholders sin resolver están permitidos. Una factura o un correo transaccional debería usar substitute. Una vista previa puede usar safe_substitute para mostrar los campos aún incompletos.

Probar las plantillas

Las pruebas deben cubrir renderizado correcto, claves ausentes, placeholders repetidos, dólares escapados mediante $$, identificadores junto a otros caracteres y sintaxis inválida.

def test_mensaje_pago():
    plantilla = Template("$nombre pagó $$ $cantidad")
    resultado = plantilla.substitute(nombre="Lu", cantidad="20")
    assert resultado == "Lu pagó $ 20"

En proyectos multilingües, prueba todas las traducciones con el mismo conjunto de identificadores aprobados. La redacción cambia, pero los nombres internos pueden permanecer estables. Esto evita crear mappings distintos para cada idioma.

Formatear valores antes de sustituir

Template no incluye el formato numérico avanzado de las f-strings. Conviene preparar los valores antes de insertarlos.

precio = 129.9
datos = {"precio": f"{precio:,.2f} €", "cantidad": "2"}
mensaje = Template("Cantidad: $cantidad — Total: $precio")
print(mensaje.substitute(datos))

Este diseño mantiene las reglas de negocio y localización dentro de código probado, mientras la plantilla sigue siendo sencilla. Lo mismo se aplica a fechas, porcentajes, monedas y valores opcionales.

Errores comunes

Un error frecuente es utilizar safe_substitute en producción y permitir que marcadores incompletos lleguen al usuario. Otro es pasar un diccionario enorme con datos innecesarios o secretos. También es incorrecto asumir que Template escapa HTML o protege una consulta SQL.

No conviertas la sintaxis en un lenguaje de programación. Si necesitas bucles, condicionales, herencia, filtros y escape HTML automático, puede ser mejor un motor especializado como Jinja. Template funciona mejor cuando la tarea consiste en reemplazar variables de forma directa.

Buenas prácticas

Usa nombres descriptivos como $nombre_cliente. Documenta los campos disponibles junto al editor. Valida la plantilla al crearla o actualizarla. Mantén el escape específico del destino fuera del modelo. Reserva la sustitución segura para vistas previas y utiliza la versión estricta para el contenido final.

Guarda versiones o historial de revisiones. Un pequeño cambio de redacción puede eliminar por accidente un marcador obligatorio, por lo que la validación automática debe ejecutarse cada vez que se despliega contenido.

Conclusión

string.Template ofrece una solución pequeña, legible y deliberadamente limitada para textos configurables. No reemplaza un motor completo, pero es excelente para notificaciones, correos, informes y archivos sencillos. Al validar identificadores, exponer solo valores aprobados, formatear los datos antes del renderizado y aplicar el escape adecuado, puedes crear plantillas editables sin mezclar presentación con lógica ejecutable.

Referencias oficiales: Template strings de Python y html.escape.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Equipo sincronizado representando asyncio.Barrier en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    asyncio.Barrier: sincroniza tareas por fases

    Aprende asyncio.Barrier en Python para sincronizar tareas por fases, coordinar pipelines y gestionar cancelaciones con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    08/09/2026
    Desarrollador creando modelos con dataclasses.KW_ONLY en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    dataclasses.KW_ONLY: exige argumentos con nombre

    Aprende dataclasses.KW_ONLY en Python para exigir argumentos con nombre, evitar llamadas ambiguas y evolucionar APIs con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    08/09/2026
    Desarrollador usando operator.methodcaller en código Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    operator.methodcaller: llama métodos en pipelines

    Aprende operator.methodcaller en Python para map, sorted, callbacks, argumentos y pipelines declarativos claros y reutilizables.

    Ler mais

    Tempo de leitura: 5 minutos
    07/09/2026
    Carpetas y directorios recorridos con pathlib.Path.walk en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    pathlib.Path.walk: recorre y filtra directorios

    Aprende a recorrer directorios con pathlib.Path.walk en Python, filtrar archivos, omitir carpetas y evitar errores comunes.

    Ler mais

    Tempo de leitura: 6 minutos
    07/09/2026
    Código Python validado con enum.verify
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    enum.verify: valida reglas de Enum en Python

    Aprende enum.verify en Python para validar valores únicos, secuencias continuas y flags con nombres mediante reglas explícitas.

    Ler mais

    Tempo de leitura: 6 minutos
    06/09/2026
    Grafo de dependencias y flujo de tareas con TopologicalSorter en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    TopologicalSorter: ordena dependencias sin ciclos

    Aprende TopologicalSorter en Python para ordenar dependencias, detectar ciclos y ejecutar pipelines secuenciales o paralelos con seguridad.

    Ler mais

    Tempo de leitura: 6 minutos
    06/09/2026