codyc4321
codyc4321

Reputation: 9682

Keyerror in multiple key string interpolation in Python

I am having a problem like

In [5]: x = "this string takes two like {one} and {two}"

In [6]: y = x.format(one="one")
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-6-b3c89fbea4d3> in <module>()
----> 1 y = x.format(one="one")

KeyError: 'two'

I have a compound string with many keys that gets kept in a config file. For 8 different queries, they all use the same string, except 1 key is a different setting. I need to be able to substitute a key in that file to save the strings for later like:

"this string takes two like one and {two}"

How do I substitute one key at a time using format?

Upvotes: 0

Views: 1208

Answers (5)

codyc4321
codyc4321

Reputation: 9682

All great answers, I will start using this Template package soon. Very disappointed in the default behavior here, not understanding why a string template requires passing all the keys each time, if there are 3 keys I can't see a logical reason you can't pass 1 or 2 (but I also don't know how compilers work)

Solved by using %s for the items I'm immediately substituting in the config file, and {key} for the keys I replace later upon execution of the flask server

In [1]: issue = "Python3 string {item} are somewhat defective: %s"

In [2]: preformatted_issue = issue % 'true'

In [3]: preformatted_issue
Out[3]: 'Python3 string {item} are somewhat defective: true'

In [4]: result = preformatted_issue.format(item='templates')

In [5]: result
Out[5]: 'Python3 string templates are somewhat defective: true'

Upvotes: 0

vaultah
vaultah

Reputation: 46583

If placeholders in your string don't have any format specifications, in Python 3 you can use str.format_map and provide a mapping, returning the field name for missing fields:

class Default(dict):
    def __missing__(self, key):
        return '{' + key + '}'
In [6]: x = "this string takes two like {one} and {two}"

In [7]: x.format_map(Default(one=1))
Out[7]: 'this string takes two like 1 and {two}'

If you do have format specifications, you'll have to subclass string.Formatter and override some methods, or switch to a different formatting method, like string.Template.

Upvotes: 3

hiro protagonist
hiro protagonist

Reputation: 46901

you can escape the interpolation of {two} by doubling the curly brackets:

x = "this string takes two like {one} and {{two}}"
y = x.format(one=1)
z = y.format(two=2)
print(z) # this string takes two like 1 and 2

a different way to go are template strings:

from string import Template

t = Template('this string takes two like $one and $two')
y = t.safe_substitute(one=1)
print(y)  # this string takes two like 1 and $two
z = Template(y).safe_substitute(two=2)
print(z) # this string takes two like 1 and 2

(this answer was before mine for the template strings....)

Upvotes: 2

Moses Koledoye
Moses Koledoye

Reputation: 78564

I think string.Template does what you want:

from string import Template

s = "this string takes two like $one and $two"
s = Template(s).safe_substitute(one=1)
print(s)
# this string takes two like 1 and $two

s = Template(s).safe_substitute(two=2)
print(s)
# this string takes two like 1 and 2

Upvotes: 3

JohanL
JohanL

Reputation: 6891

You can replace {two} by {two} to enable further replacement later:

y = x.format(one="one", two="{two}")

This easily extends in multiple replacement passages, but it requires that you give all keys, in each iteration.

Upvotes: 1

Related Questions