Is it possible to do partial string formatting with the advanced string formatting methods, similar to the string template safe_substitute()
function?
F
This limitation of .format()
- the inability to do partial substitutions - has been bugging me.
After evaluating writing a custom Formatter
class as described in many answers here and even considering using third-party packages such as lazy_format, I discovered a much simpler inbuilt solution: Template strings
It provides similar functionality but also provides partial substitution thorough safe_substitute()
method. The template strings need to have a $
prefix (which feels a bit weird - but the overall solution I think is better).
import string
template = string.Template('${x} ${y}')
try:
template.substitute({'x':1}) # raises KeyError
except KeyError:
pass
# but the following raises no error
partial_str = template.safe_substitute({'x':1}) # no error
# partial_str now contains a string with partial substitution
partial_template = string.Template(partial_str)
substituted_str = partial_template.safe_substitute({'y':2}) # no error
print substituted_str # prints '12'
Formed a convenience wrapper based on this:
class StringTemplate(object):
def __init__(self, template):
self.template = string.Template(template)
self.partial_substituted_str = None
def __repr__(self):
return self.template.safe_substitute()
def format(self, *args, **kws):
self.partial_substituted_str = self.template.safe_substitute(*args, **kws)
self.template = string.Template(self.partial_substituted_str)
return self.__repr__()
>>> s = StringTemplate('${x}${y}')
>>> s
'${x}${y}'
>>> s.format(x=1)
'1${y}'
>>> s.format({'y':2})
'12'
>>> print s
12
Similarly a wrapper based on Sven's answer which uses the default string formatting:
class StringTemplate(object):
class FormatDict(dict):
def __missing__(self, key):
return "{" + key + "}"
def __init__(self, template):
self.substituted_str = template
self.formatter = string.Formatter()
def __repr__(self):
return self.substituted_str
def format(self, *args, **kwargs):
mapping = StringTemplate.FormatDict(*args, **kwargs)
self.substituted_str = self.formatter.vformat(self.substituted_str, (), mapping)