1

Python has this concept of string replacement fields such as mystr1 = "{replaceme} other text..." where {replaceme} (the replacement field) can be easily formatted via statements such as mystr1.format(replaceme="yay!").

So I often am working with large strings and sometimes do not know all of the replacement fields and need to either manually resolve them which is not too bad if it is one or two, but sometimes it is dozens and would be nice if python had a function similar to dict.keys().

How does one to parse string replacement fields in a string in python?

John Drinane
  • 1,279
  • 2
  • 14
  • 25

3 Answers3

1

In lieu of answers from the community I wrote a helper function below to spit out the replacement fields to a dict which I can then simply update the values to what I want and format the string.

Is there a better way or built in way to do this?

cool_string = """{a}yo{b}ho{c}ho{d}and{e}a{f}bottle{g}of{h}rum{i}{j}{k}{l}{m}{n}{o}{p}{q}{r}{s}{t}{u}{v}{w}{x}{y}{z}"""
def parse_keys_string(s,keys={}):
    try:
        print(s.format(**keys)[:0])
        return keys
    except KeyError as e:
        print("Adding Key:",e)
        e = str(e).replace("'","")
        keys[e]=e
        parse_keys_string(s,keys)
        return keys
cool_string_replacement_fields_dict = parse_keys_string(cool_string)
#set replacement field values
i = 1
for k,v in cool_string_replacement_fields_dict.items():
    cool_string_replacement_fields_dict[k] = i
    i = i + 1
#format the string with desired values...
cool_string_formatted = cool_string.format(**cool_string_replacement_fields_dict)
print(cool_string_formatted)
John Drinane
  • 1,279
  • 2
  • 14
  • 25
1

I came up with the following:

class NumfillinDict(dict):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.i = -1
    def __missing__(self, key): #optionally one could have logic based on key
        self.i+=1
        return f"({self.i})"
    
cool_string = ("{a}yo{b}ho{c}ho{d}and{e}a{f}bottle{g}of{h}rum{i}\n"
               "{j}{k}{l}{m}{n}{o}{p}{q}{r}{s}{t}{u}{v}{w}{x}{y}{z}")
dt = NumfillinDict(notneeded='something', b=' -=actuallyIknowb<=- ')
filled_string = cool_string.format_map(dt)
print(filled_string)

It works a bit like a defaultdict by filling in missing key-value pairs using the __missing__ method.

Result:

(0)yo -=actuallyIknowb<=- ho(1)ho(2)and(3)a(4)bottle(5)of(6)rum(7)
(8)(9)(10)(11)(12)(13)(14)(15)(16)(17)(18)(19)(20)(21)(22)(23)(24)

Inspired by: Format string unused named arguments

MJW
  • 51
  • 3
0

at first used @John Drinane's, adapted (and I wanted a list)

def get_fmt_vars(txt: str, vars_dict: dict = {}) -> list:
    try:
        txt.format(**vars_dict)
    except KeyError as err:
        vars_dict[eval(err.__str__())] = ''
        get_fmt_vars(txt, vars_dict)
    return list(vars_dict)

.

then followed @MJW's link, that helped me making better sense of the docs, in particular

parse(format_string)

Loop over the format_string and return an iterable of tuples (literal_text, field_name, format_spec, conversion)

ended up with

import string

foo_txt = '{1} {var_2} some more stuff here {levelname: <9}'

[k[1] for k in string.Formatter().parse(format_string=foo_txt)]

where k[1] gets the field_name

output:

['1', 'var_2', 'levelname']

.

I believe it's better (and no eval), but same question:

is there a better way?

lexc
  • 61
  • 3