Template string is another method used to format strings in Python. In comparison with %operator, .format() and f-strings, it has a (arguably) simpler syntax and functionality. It’s ideal for internationalization (i18n), and there are some nuances that you may find advantageous—especially when working with regular expressions (regex).
Let’s first see the syntax of the other three, so we can compare.
% operator
>>> name = "Alfredo"
>>> age = 40
>>> "Hello, %s. You are %s." % (name, age)
'Hello, Alfredo. You are 40.'
.format()
>>> print('The {} {} {}'.format('car','yellow','fast')) #empty braces
The car yellow fast
>>> print('The {2} {1} {0}'.format('car','yellow','fast')) #index
The fast yellow car
>>> print('The {f} {y} {c}'.format(c='car',y='yellow',f='fast')) #keywords
The fast yellow car
f-strings
>>> name = "Peter"
>>> print(f'Nice to meet you, {name}')
Template string
It uses Template class from string module. It has a syntax somewhat similar to .format() when done with keywords, but instead of curly braces to define the placeholder, it utilises a dollar sign ($). ${} is also valid and should be in place when a valid string comes after the placeholder.
See the syntax for various situations. I’ll begin explaining safe_substitute and the use with regex.
safe_substitute and Regex
Imagine you want to replace something in a string that contains {}, % and $. It can happen when working with regular expression.
Consider an input field that accepts company stock symbol plus positive (+) or negative (-) values in percentages and nothing afterwards. And symbol plus +- is dynamically replaced. Such as: AAPL: +20% or TSLA: -5% (Perhaps it’s a silly use case, I know! but please ignore. It’s just an example).
Regex: (([symbol: + or -][0–9]{1,4}[%])$)
For % operator, .format() and f-string – it doesn’twork. % operator and .format() raises an error and f-strings removes {m,n}. See below
#% operator
>>> print('(([%s][0-9]{1,4}[%])$)' % "AAPL: -")
TypeError: not enough arguments for format string
#.format()
>>> print('(([{symbol_pos_neg}][0-9]{1,4}[%])$)'.format(symbol_pos_neg = 'AAPL: +'))
KeyError: '1,4'
#f-strings
>>> symbol_pos_neg = "AAPL: +"
>>> print(f'(([{symbol_pos_neg}][0-9]{1,4}[%])$)')
(([AAPL: +][0-9](1, 4)[%])$)
Although it can be easily solved by escaping the characters (doubling the curly braces or percentage sign), I find it inconvenient. With Template string, you don’t need to do that. {} and % are not used to define placeholders, and by using safe_substitute, you’re not enforced to replace all $.
See below, the regex is unchanged and $symbol_pos_neg is replaced correctly.
>>> print(Template('(([$symbol_pos_neg][0-9]{1,4}[%])$)').safe_substitute(symbol_pos_neg='AAPL: -'))
(([AAPL: -][0-9]{1,4}[%])$)
Regular case with keyword
>>> from string import Template
>>> Template('$obj is $colour').substitute(obj='Car',colour='red')
'Car is red'
Regular case with a dictionary
>>> d = dict(obj='Car')
>>> Template('$obj is red').substitute(d)
'Car is red'
Placeholder followed by an invalid character
If there is an invalid string after the placeholder, only the placeholder is considered. Example, see the ‘.’ (dot) after $who. It’s printed as normal.
>>> from string import Template
>>> Template('$obj. is $colour').substitute(obj='Car',colour='red')
'Car. is red'
Placeholder followed by a valid character
If valid characters follow the placeholder, it must be enclosed in curly braces.
>>> from string import Template
>>> Template('${noun}ification').substitute(noun='Ident')
'Identification'
To note, characters such as underscore (_) is also considered valid. So the same rule to use ${} applies.
Multiple $$$ signs
The substitution is done as normal, and one extra $ is printed.
>>> Template('$obj. is $$$colour').substitute(obj='Car',colour='red')
'Car. is $red'
Final Thoughts
I’m still a beginner in Python and comparing String formatting methods taught me a lot. I hope I was clear in providing the details to you. Despite Template string being less powerful:
viniciusmonteiro$ python3 -m timeit -s "x = 'f'; y = 'z'" "f'{x} {y}'"
5000000 loops, best of 5: 82.5 nsec per loop
viniciusmonteiro$ python3 -m timeit -s "from string import Template; x = 'f'; y = 'z'" "Template('$x $y').substitute(x=x, y=y)" # template string
500000 loops, best of 5: 752 nsec per loop
I think it brings some benefit in terms of simplicity and convenience in some cases. Although I understand these can be subjective.
Reference
[1] [string](https://docs.python.org/3/library/string.html#module-string) – Common string operations https://docs.python.org/3/library/string.html#template-strings
[2] Python 3’s f-Strings: An Improved String Formatting Syntax (Guide) https://realpython.com/python-f-strings/
[3] Performance of different string concatenation methods in Python – why f-strings are awesome https://grski.pl/fstrings-performance.html






