Home > Software design >  .format() string, keep curly brackets if no match available
.format() string, keep curly brackets if no match available

Time:02-26

I try to format a string with curly brackets like this:

dct_mapper = dict(a=1)
s = '{key1: value2, key1: value2}; attrib1={a}; attrib2={b}'
s.format(**dct_mapper)

Results in:
# KeyError: 'key1'

Expected:
# '{key1: value2, key1: value2}; attrib1=1; attrib2={b}'

I tried defaultdict from the collections package and also into .format_map(), then plaed around with regex trying to replace the brackets with additional ones, which feels less like a solution and more like a hack and also doesn't work if you have multiple repeating brackets in the string to begin with.

It is not a json string, because then I could have used the json library to map the values.

Does anyone have an idea how to solve this?

I currently consider using a loop and str.replace('{a}', 1) but this feels clunky as well.

CodePudding user response:

In case you don't want to convert {} to {{}}, I suggest you using Template from string. Its interface allows you to custom the formatting options as I did below:

from string import Template


class MyTemplate(Template):
    delimiter = ""


dct_mapper = dict(a=1)
s = MyTemplate('{key1: value2, key1: value2}; attrib1={a}; attrib2={b}')
print(s.safe_substitute(**dct_mapper))

The output is, as you wanted: {key1: value2, key1: value2}; attrib1=1; attrib2={b}

Explanation: By default i Template you format strings using $var instead of {var} so I changed the pattern of the variable name (after the $) to be ${var} and the delimiter itself to be "", so {var} is enough instead of ${var}.

Moreover, templates have safe_substitute which allows you to format only few variables in the entire string and it doesn't raise an error for non-existing variable formats.

Also, s.safe_substitute(**dct_mapper) can be changed into s.safe_substitute(dct_mapper) if you prefer this over that.

CodePudding user response:

Your format string was invalid but assume you wanted to actually print the braces in that case. Double the braces to print one literal brace.

You can subclass dict to just return the braced format key if the key isn't present. This fills out {a} but leaves {b} alone:

class MyDict(dict):

    # "magic" method called when a key is missing from the dict
    def __missing__(self, key):
        return f'{{{key}}}'

dct_mapper = MyDict(a=1)
s = '{{key1: value2, key1: value2}}; attrib1={a}; attrib2={b}'
# Must use format_map.  format(**dct_mapper) doesn't work
print(s.format_map(dct_mapper))

Output:

{key1: value2, key1: value2}; attrib1=1; attrib2={b}

CodePudding user response:

Is this what you are looking for?

dct_mapper = dict(a=1, b=2)
s = '{{key1: value2, key1: value2; attrib1={a}; attrib2={b}}}'.format(**dct_mapper)

Output: {key1: value2, key1: value2; attrib1=1; attrib2=2}

dct_mapper = dict(a=1, b=1)
s = '{{key1: value2, key1: value2; attrib1={a}; attrib2={b}}}'.format(**dct_mapper)

Output: {key1: value2, key1: value2; attrib1=1; attrib2=1}

But if you try to pass only 1 value, say for just "a", it will throw an error because format() is going to fill in the values given to all the {} in the string.

dct_mapper = dict(a=1)
s = '{{key1: value2, key1: value2; attrib1={a}; attrib2={b}}}'.format(**dct_mapper)

Output: KeyError: 'b'

Hope this helps!

  • Related