Introduction to string templates

A string template acts as a sketch of how the final string should look. It is a pattern that is used as a basis for creating other strings. String templates usually includes tags or placeholders that represent a certain set of text that will be replaced with the appropriate value when the actual string is created.

Python offers some various tools for creating string templates some of which you might already be familiar with. They includes:

 %-formatting:

This is a popular formatting style  that uses the '%' operator as a placeholder for variable values.

def add(a, b):
   result =  "%s + %s = %s"%(a, b, a + b)
   return result

print(add(10, 20))

As you can see above, the % placeholders gets replaced with the actual values when using the template for creating a string.

str.format():

The str class defines the format() method which takes the passed arguments, formats them, and places them in the string where the {} placeholders are:  

S = "This is an example of {}"

print(S.format("string formatting")) 

f-Strings (Formatted String Literals):

This is a relatively new string formatting method in Python (introduced in Python 3.6) that makes use of string literals prefixed with an ‘f’. F-strings provide a simple way to embed expressions inside of string literals.

value = "string formatting"

print(f"This is an example of {value}")

The string.Template class

The Template class available  in the string module enables the use of advanced and more expressive way of templating strings. It provides a powerful set of features for customizing how strings are templated, allowing us to perform complex substitutions and formatting operations. 

The string module is freely available in the standard library and we, therefore, just need to import it in our program without extra installations.

#import the string module
import string

#Create template string
data = { 'name': 'John Smith', 'age': 33 } 
template = string.Template("${name} is ${age} years old")

print(template.substitute(data))

Basic Syntax

Constructing the template:

We start by creating an instance of the Template class and passing in a string containing placeholders By convention we enclose the placeholders in curly braces ({}) in order tp distinguish them from other words in the template string.

Template(template)
template A required string that containing the necessary placeholders. 
**kwargs Any other necessary keyword arguments to be used in the formating such as delimiter
import string

my_template = string.Template("your name is ${name} and your age is ${age}")
print(my_template.template)

By default, the Template class uses the dollar sign("$") character as the placeholder delimiter but we can change this as we will see later in Advanced templates

Substituting a value into the template

This is done by calling the substitute() method on the template string, and passing in a dictionary with the desired values, or alternatively, passing the substitute values as keyword arguments.

import string

#create the template
my_template = string.Template("your name is ${name} and your age is ${age}")

#perform the substitution
final_string = my_template.substitute(name= "John Doe", age = 30)

print(final_string)

The substitute() method have the following syntax:

substitute(mapping={}, **kwargss)

Where, mapping is an optional argument which is given if we are not passing the substitute values as keyword argument. Likewise, the **kwargs arguments refers to the substitute values passes as keyword arguments if we are not passing them in the mapping dictionary. 

import string 

data = {"name": "John Doe", "city": "New York"} 
# Create a string template
template = string.Template("${name} lives in ${city}") 

# Use substitute to replace placeholders with actual values 
substituted_string = template.substitute(data)

print(substituted_string)

 A practical example

import string

def evaluate(a, b, oper="+"):
     output_template = string.Template("${a} ${oper} ${b} = ${result}")
     
     result = None
     match oper:
         case "+":
             result = a + b
         case "-":
             result = a - b
         case "x":
             result = a * b
         case "/":
             result = a / b
         case _:
             return "Invalid operator"
     return output_template.substitute({"a": a , "b": b, "oper": oper, "result": result})

print(evaluate(20, 10))
print(evaluate(20, 10, "-"))
print(evaluate(20, 10, "x"))
print(evaluate(20, 10, "/"))

Safe Substitution

The substitute() method will raise a KeyError exception if we do not provide any of the defined substitute values.

import string

template_string = "${a}, ${b}"

template = string.Template(template_string)

template.substitute(a = 2)

We can avoid the error with safe substitution using the safe_substitute() method. This method replaces all placeholders in the template string, but will leave unknown placeholders intact. This makes it possible to use templates even when not all possible placeholders exist.

import string

data = {"name": "John Doe", "age": 30, "country": "Finland"}

template_string = "My name is ${name}, I am ${age} years old, I live in ${country} and I am married to ${spouse}"

my_template = string.Template(template_string)

print(my_template.safe_substitute(data))

Get all the placeholder names

The get_identifiers() method returns a list containing all the placeholder names used in the template string.

import string

template_string = "My name is ${name}, I am ${age} years old, I live in ${country} and I am married to ${spouse}"

my_template = string.Template(template_string)

print(my_template.get_identifiers())

Advanced templates

The default way that the string.Template class works can be overridden by adjusting the  the regex pattern that it uses to find the placeholder names in the template string. A direct way to achieve this is by simply changing the delimiter and  idpattern class attributes.

import string

template_string = """
name : @_name
age: @_age
country: @_country
spouse: @_spouse
"""

data = {"_name": "John Doe", "_age": 30, "_country": "Finland", "_spouse": "Mary Doe"} 

class MyTemplate(string.Template):
     delimiter = "@"
     idpattern = "_[a-z]+"

my_template = MyTemplate(template_string)

final_string = my_template.substitute(data)
print(final_string)

In the above example, substitution rules are changed so that the delimiter is '@' instead of the default  '$' and placeholder names must start with an underscore.

You can define more complex rules by setting an entirely new regular expressions as the idpattern.