The Python Oracle

partial string formatting

Become part of the top 3% of the developers by applying to Toptal https://topt.al/25cXVn

--

Music by Eric Matyas
https://www.soundimage.org
Track title: Light Drops

--

Chapters
00:00 Question
00:25 Accepted answer (Score 72)
00:57 Answer 2 (Score 159)
01:16 Answer 3 (Score 159)
01:40 Answer 4 (Score 63)
03:27 Thank you

--

Full question
https://stackoverflow.com/questions/1128...

Answer 3 links:
[lazy_format]: https://pypi.python.org/pypi/lazy_format
[Template strings]: https://docs.python.org/2/library/string...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #stringformatting

#avk47



ANSWER 1

Score 167


If you know in what order you're formatting things:

s = '{foo} {{bar}}'

Use it like this:

ss = s.format(foo='FOO') 
print ss 
>>> 'FOO {bar}'

print ss.format(bar='BAR')
>>> 'FOO BAR'

You can't specify foo and bar at the same time - you have to do it sequentially.




ACCEPTED ANSWER

Score 77


You can trick it into partial formatting by overwriting the mapping:

import string

class FormatDict(dict):
    def __missing__(self, key):
        return "{" + key + "}"

s = '{foo} {bar}'
formatter = string.Formatter()
mapping = FormatDict(foo='FOO')
print(formatter.vformat(s, (), mapping))

printing

FOO {bar}

Of course this basic implementation only works correctly for basic cases.




ANSWER 3

Score 67


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)



ANSWER 4

Score 29


Not sure if this is ok as a quick workaround, but how about

s = '{foo} {bar}'
s.format(foo='FOO', bar='{bar}')

? :)