Template String

Imagine que você criou uma automação para envio de email aos funcionários da empresa que você trabalha e que no corpo desse email existem dados que precisam ser alterados dinamicamente, exatamente aqui Template String entra em ação.

Veja esse caso de uso:

from string import Template  
  
tmpl_email = """  

Caro $employee_name,  
  
Espero que este e-mail o encontre bem. Gostaríamos de informar que realizamos uma revisão salarial anual e,  
como resultado, houve um ajuste no seu salário com base no índice de correção pela inflação.  
  
Após análises e cálculos cuidadosos, verificamos que o índice de inflação acumulada nos últimos 12 meses foi de  
$idx_inflation_rate. Com base nesse valor, o seu salário será corrigido para refletir a variação do custo de vida e  
garantir que você seja devidamente compensado.  
  
A partir do próximo mês, seu salário corrigido será de $salary. Essa correção foi aplicada retroativamente, o que s  
ignifica que você também receberá um valor adicional referente ao período desde a última atualização salarial.  
  
Se você tiver alguma dúvida sobre o cálculo ou qualquer outro aspecto relacionado à sua remuneração, fique à vontade  
para entrar em contato com o setor de Recursos Humanos. Estamos aqui para ajudar e fornecer qualquer esclarecimento  
adicional que você precise.  
  
Agradecemos sua dedicação e contribuição para a empresa. Reconhecemos que seu trabalho é fundamental  
para o nosso sucesso contínuo, e a atualização salarial é uma forma de valorizarmos seus esforços.  
  
Parabenizamos pelo seu desempenho e estamos ansiosos para continuar trabalhando juntos.  
  
Atenciosamente,  
  
$sender_name  
$sender_role  
$sender_company  
  
"""  
to_replace = {  
'employee_name': 'João da Silva',  
'idx_inflation_rate': '5%',  
'salary': 'R$ 1.500,00',  
'sender_name': 'Maria de Souza',  
'sender_role': 'Gerente de RH',  
'sender_company': 'Empresa XYZ'  
}  
  
to_replace_two = {  
'employee_name': 'João da Silva',  
'idx_inflation_rate': '5%',  
'salary': 'R$ 1.500,00',  
'sender_name': 'Maria de Souza',  
'sender_role': 'Gerente de RH',  
}  
  
tmpl_email = Template(tmpl_email)  
  
print(tmpl_email.substitute(to_replace))  
print(tmpl_email.safe_substitute(to_replace_two))   

Como podemos notar ao instanciarmos o objeto Template passamos no construtor um modelo contendo a “marcação” de quais campos queremos interpolar, na implementação da classe podemos ver que essa “marcação” leva o nome de “delimiter” e que por padrão seu valor é $.

class Template:
    """A string class for supporting $-substitutions."""

    delimiter = '$'
    # r'[a-z]' matches to non-ASCII letters when used with IGNORECASE, but
    # without the ASCII flag.  We can't add re.ASCII to flags because of
    # backward compatibility.  So we use the ?a local flag and [a-z] pattern.
    # See https://bugs.python.org/issue31672
    idpattern = r'(?a:[_a-z][_a-z0-9]*)'
    braceidpattern = None
    flags = _re.IGNORECASE

Então na prática as strings que serão substituídas devem iniciar com o delimitador $, caso você não criei um classe personalizada para isso.

Agora falando em substituir existem dois métodos disponíveis para isso:

string.substitute()

string.safe_substitute()

O método substitute() irá lançar uma exceção do tipo KeyError caso algum campo não seja encontrado no objeto que você passou como parâmetro, veja o exemplo abaixo onde o campo sender_company não foi passado no objeto to_replace_two.

print(tmpl_email.substitute(to_replace_two)) 

#output
Traceback (most recent call last):
  File "/home/python/app/src/template_string.py", line 52, in <module>
    print(tmpl_email.substitute(to_replace_two))
  File "/usr/local/lib/python3.10/string.py", line 121, in substitute
    return self.pattern.sub(convert, self.template)
  File "/usr/local/lib/python3.10/string.py", line 114, in convert
    return str(mapping[named])
KeyError: 'sender_company'

Agora caso não queira esse comportamento você pode utilizar o método safe_substitute() que irá ignorar os campos que não forem encontrados no objeto passado como parâmetro, retornando a string original.

print(tmpl_email.safe_substitute(to_replace_two))

#output
...
...
...
Agradecemos sua dedicação e contribuição para a empresa. Reconhecemos que seu trabalho é fundamental 
para o nosso sucesso contínuo, e a atualização salarial é uma forma de valorizarmos seus esforços.

Parabenizamos pelo seu desempenho e estamos ansiosos para continuar trabalhando juntos.

Atenciosamente,

Maria de Souza
Gerente de RH
$sender_company

Podemos inclusive usar com objetos mais complexos:

from string import Template

dados_cliente = {
    "nome": "João da Silva",
    "idade": 35,
    "endereco": {
        "rua": "Rua Principal",
        "cidade": "São Paulo",
        "estado": "SP"
    },
    "pedidos": ["Item 1", "Item 2", "Item 3"]
}

tmpl_string = """
Olá, $nome!

Agradecemos por sua compra recente. Abaixo estão os detalhes do seu pedido:

- Itens comprados:
  $itens

- Endereço de entrega:
  Rua: $endereco_rua
  Cidade: $endereco_cidade
  Estado: $endereco_estado

Atenciosamente,
Empresa XYZ
"""


tmpl = Template(tmpl_string)

formatted = tmpl.substitute(
    nome=dados_cliente["nome"],
    itens="\n  ".join(dados_cliente["pedidos"]),
    endereco_rua=dados_cliente["endereco"]["rua"],
    endereco_cidade=dados_cliente["endereco"]["cidade"],
    endereco_estado=dados_cliente["endereco"]["estado"]
)

print(formatted)

#output

Olá, João da Silva!

Agradecemos por sua compra recente. Abaixo estão os detalhes do seu pedido:

- Itens comprados:
  Item 1
  Item 2
  Item 3

- Endereço de entrega:
  Rua: Rua Principal
  Cidade: São Paulo
  Estado: SP

Atenciosamente,
Empresa XYZ

Template String com Classe Personalizada

E para finalizarmos quero mostrar como podemos utilizar uma classe personalizada para fazer a interpolação dos campos. Você pode fazer isso criando uma classe que herde de Template e sobrescreva o atributo delimiter, veja o exemplo abaixo:

from string import Template

class MyTemplate(Template):
    delimiter = '%'

dados_cliente = {
    "nome": "João da Silva",
    "idade": 35,
    "endereco": {
        "rua": "Rua Principal",
        "cidade": "São Paulo",
        "estado": "SP"
    },
    "pedidos": ["Item 1", "Item 2", "Item 3"]
}

tmpl_string = """
Olá, %nome!

Agradecemos por sua compra recente. Abaixo estão os detalhes do seu pedido:

- Itens comprados:
  %itens

- Endereço de entrega:
  Rua: %endereco_rua
  Cidade: %endereco_cidade
  Estado: %endereco_estado

Atenciosamente,
Empresa XYZ
"""


tmpl = MyTemplate(tmpl_string)

formatted = tmpl.substitute(
    nome=dados_cliente["nome"],
    itens="\n  ".join(dados_cliente["pedidos"]),
    endereco_rua=dados_cliente["endereco"]["rua"],
    endereco_cidade=dados_cliente["endereco"]["cidade"],
    endereco_estado=dados_cliente["endereco"]["estado"]
)

print(formatted)

#output

Olá, João da Silva!

Agradecemos por sua compra recente. Abaixo estão os detalhes do seu pedido:

- Itens comprados:
  Item 1
  Item 2
  Item 3

- Endereço de entrega:
  Rua: Rua Principal
  Cidade: São Paulo
  Estado: SP

Atenciosamente,
Empresa XYZ

"""

Como pode ver esse é um recurso muito útil e inclusive muito utilizado em softwares que dão suporte a internacionalização i18n.

Por hoje é isso, espero que tenha gostado e até a próxima.

Fonte: https://docs.python.org/pt-br/3.10/library/string.html