21

What is wrong with this piece of code?

dic = { 'fruit': 'apple', 'place':'table' }
test = "I have one {fruit} on the {place}.".format(dic)
print(test)

>>> KeyError: 'fruit'
funnydman
  • 9,083
  • 4
  • 40
  • 55
bogdan
  • 9,056
  • 10
  • 37
  • 42
  • 1
    Possible duplicate of https://stackoverflow.com/questions/5952344/how-do-i-format-a-string-using-a-dictionary-in-python-3-x – Frank May 16 '19 at 20:17

3 Answers3

42

Should be

test = "I have one {fruit} on the {place}.".format(**dic)

Note the **. format() does not accept a single dictionary, but rather keyword arguments.

jfs
  • 399,953
  • 195
  • 994
  • 1,670
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • Thanks, it works. Can you update the answer to explain why I have to add `**` before the dictionary? – bogdan Jun 03 '11 at 16:08
  • 4
    @bogdan, the `**` indicates that the dictionary should be expanded to a list of keyword parameters. The `format` method doesn't take a dictionary as a parameter, but it does take keyword parameters. – Mark Ransom Jun 03 '11 at 16:14
  • @bogdan: Added two links to my answer. The reason basically is "because the documentation says so". – Sven Marnach Jun 03 '11 at 16:14
  • 1
    @bogdan That just tells Python that you are providing a dictionary to the function so that it can provide the values in the dictionary, rather than the dictionary itself. You can do the same when calling any function. If function 'f' takes the args 'a','b', and 'c', you could use `dic = {'a':1,'b':2,'c':3}` and call `f(**dic)`. – Michael Smith Jun 03 '11 at 16:19
  • 4
    The reason is that `"I have one {0[fruit]} on the {0[place]}.".format(dic)` works too - `dic` is the 0th positional argument here and you can access it's keys in the template. – Jochen Ritzel Jun 03 '11 at 16:24
11

There is ''.format_map() function since Python 3.2:

test = "I have one {fruit} on the {place}.".format_map(dic)

The advantage is that it accepts any mapping e.g., a class with __getitem__ method that generates values dynamically or collections.defaultdict that allows you to use non-existent keys.

It can be emulated on older versions:

from string import Formatter

test = Formatter().vformat("I have one {fruit} on the {place}.", (), dic)
jfs
  • 399,953
  • 195
  • 994
  • 1,670
1

You can use the following code too:

dic = { 'fruit': 'apple', 'place':'table' }
print "I have one %(fruit)s on the %(place)s." % dic

If you want to know more about format method use: http://docs.python.org/library/string.html#formatspec

Artsiom Rudzenka
  • 27,895
  • 4
  • 34
  • 52
  • 3
    The `%` operator shouldn't be used in new code according to the Python documentation. – Mark Ransom Jun 03 '11 at 16:13
  • Could you please give me a reference so i will be able to read more on this? – Artsiom Rudzenka Jun 03 '11 at 16:14
  • Thank you very much), will try to avoid using it from now – Artsiom Rudzenka Jun 03 '11 at 16:21
  • @Mark: `%` was meant to be deprecated in Python 3.1, but they never did. It won't go away soon (or probably not at all), so it's ok to still use it. – Sven Marnach Jun 03 '11 at 16:30
  • @Sven, good to know, thanks. Still I'd go with `format` first because it's recommended, second because it seems cleaner. – Mark Ransom Jun 03 '11 at 16:34
  • @MarkRansom it is absolutely required to use `%` format in many cases. One example is: `logging.debug("Message with %(expensive_out)s" % expensive_out=expensive_func())` If f-string format is used, `expensive_func()` will be called only to have the constructed string thrown away (assuming debug logging is disabled). – Ajay M Feb 28 '23 at 01:29