259

I am a big fan of using dictionaries to format strings. It helps me read the string format I am using as well as let me take advantage of existing dictionaries. For example:

class MyClass:
    def __init__(self):
        self.title = 'Title'

a = MyClass()
print 'The title is %(title)s' % a.__dict__

path = '/path/to/a/file'
print 'You put your file here: %(path)s' % locals()

However I cannot figure out the python 3.x syntax for doing the same (or if that is even possible). I would like to do the following

# Fails, KeyError 'latitude'
geopoint = {'latitude':41.123,'longitude':71.091}
print '{latitude} {longitude}'.format(geopoint)

# Succeeds
print '{latitude} {longitude}'.format(latitude=41.123,longitude=71.091)
Alex L
  • 8,748
  • 5
  • 49
  • 75
Doran
  • 4,051
  • 2
  • 27
  • 41

9 Answers9

507

Is this good for you?

geopoint = {'latitude':41.123,'longitude':71.091}
print('{latitude} {longitude}'.format(**geopoint))
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
cocoatomo
  • 5,432
  • 2
  • 14
  • 12
  • 3
    Tried this and it worked. But I don't understand the use of the 'pointer notation'. I know Python doesn't use pointers, is this an example of kwargs? – Homunculus Reticulli Jun 13 '12 at 13:23
  • 4
    @HomunculusReticulli That is a format parameter (Minimum field width), not a pointer to a pointer C++ style. http://docs.python.org/release/2.4.4/lib/typesseq-strings.html – D.Rosado Jul 06 '12 at 13:18
  • 36
    Python 3.2 introduced [`format_map`](https://docs.python.org/3.4/library/stdtypes.html#str.format_map). _Similar to `str.format(**mapping)`, except that `mapping` is used directly and not copied to a `dict`. This is useful if for example `mapping` is a dict subclass_ – diapir Jan 17 '15 at 14:25
  • 1
    @eugene What does ** do to a python dictionary? I don't think that it creates an object because print(**geopoint) fails giving syntax error – Nityesh Agarwal Apr 08 '17 at 18:35
  • 9
    @NityeshAgarwal it spreads the dictionary with the name=value pairs as individual arguments i.e. `print(**geopoint)` is same as `print(longitude=71.091, latitude=41.123)`. In many languages, it is known as *splat operator*. In JavaScript, it's called *spread operator*. In python, there is no particular name given to this operator. – abhisekp Apr 15 '17 at 12:01
  • @NityeshAgarwal new to me too. it's pretty [cool](https://repl.it/@SamyBencherif/BoldMoccasinOperation). – Samie Bencherif Dec 01 '18 at 02:37
88

As Python 3.0 and 3.1 are EOL'ed and no one uses them, you can and should use str.format_map(mapping) (Python 3.2+):

Similar to str.format(**mapping), except that mapping is used directly and not copied to a dict. This is useful if for example mapping is a dict subclass.

What this means is that you can use for example a defaultdict that would set (and return) a default value for keys that are missing:

>>> from collections import defaultdict
>>> vals = defaultdict(lambda: '<unset>', {'bar': 'baz'})
>>> 'foo is {foo} and bar is {bar}'.format_map(vals)
'foo is <unset> and bar is baz'

Even if the mapping provided is a dict, not a subclass, this would probably still be slightly faster.

The difference is not big though, given

>>> d = dict(foo='x', bar='y', baz='z')

then

>>> 'foo is {foo}, bar is {bar} and baz is {baz}'.format_map(d)

is about 10 ns (2 %) faster than

>>> 'foo is {foo}, bar is {bar} and baz is {baz}'.format(**d)

on my Python 3.4.3. The difference would probably be larger as more keys are in the dictionary, and


Note that the format language is much more flexible than that though; they can contain indexed expressions, attribute accesses and so on, so you can format a whole object, or 2 of them:

>>> p1 = {'latitude':41.123,'longitude':71.091}
>>> p2 = {'latitude':56.456,'longitude':23.456}
>>> '{0[latitude]} {0[longitude]} - {1[latitude]} {1[longitude]}'.format(p1, p2)
'41.123 71.091 - 56.456 23.456'

Starting from 3.6 you can use the interpolated strings too:

>>> f'lat:{p1["latitude"]} lng:{p1["longitude"]}'
'lat:41.123 lng:71.091'

You just need to remember to use the other quote characters within the nested quotes. Another upside of this approach is that it is much faster than calling a formatting method.

88

To unpack a dictionary into keyword arguments, use **. Also,, new-style formatting supports referring to attributes of objects and items of mappings:

'{0[latitude]} {0[longitude]}'.format(geopoint)
'The title is {0.title}s'.format(a) # the a from your first example
  • 3
    I find this answer better, as adding the positional index for the placeholder makes the code more explicit, and easier to use. Especially if one has something like this: `'{0[latitude]} {1[latitude]} {0[longitude]} {1[longitude]}'.format(geopoint0, geopoint1)` – Løiten Sep 06 '16 at 13:01
  • 1
    This is useful if you're using a `defaultdict` and don't have all the keys – Whymarrh Jan 09 '17 at 17:12
40
print("{latitude} {longitude}".format(**geopoint))
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
S.Lott
  • 384,516
  • 81
  • 508
  • 779
39

Since the question is specific to Python 3, here's using the new f-string syntax, available since Python 3.6:

>>> geopoint = {'latitude':41.123,'longitude':71.091}
>>> print(f'{geopoint["latitude"]} {geopoint["longitude"]}')
41.123 71.091

Note the outer single quotes and inner double quotes (you could also do it the other way around).

Wyrmwood
  • 3,340
  • 29
  • 33
7

The Python 2 syntax works in Python 3 as well:

>>> class MyClass:
...     def __init__(self):
...         self.title = 'Title'
... 
>>> a = MyClass()
>>> print('The title is %(title)s' % a.__dict__)
The title is Title
>>> 
>>> path = '/path/to/a/file'
>>> print('You put your file here: %(path)s' % locals())
You put your file here: /path/to/a/file
Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
4
geopoint = {'latitude':41.123,'longitude':71.091}

# working examples.
print(f'{geopoint["latitude"]} {geopoint["longitude"]}') # from above answer
print('{geopoint[latitude]} {geopoint[longitude]}'.format(geopoint=geopoint)) # alternate for format method  (including dict name in string).
print('%(latitude)s %(longitude)s'%geopoint) # thanks @tcll
Sheikh Abdul Wahid
  • 2,623
  • 2
  • 25
  • 24
  • 1
    you missed one ;) `print('%(latitude)s %(longitude)s'%geopoint)` this is also significantly faster than the other 2 – Tcll Apr 17 '20 at 22:16
  • @tcll Actually I wanted the examples, where I can use the dictionary name inside the string. Something like this `'%(geopoint["latitude"])s %(geopoint["longitude"])s'%{"geopoint":geopoint}` – Sheikh Abdul Wahid Apr 21 '20 at 14:41
4

Use format_map to do what you want

print('{latitude} {longitude}'.format_map(geopoint))

This has the advantage that

  • the dictionary does not have to be blown up into parameters (compared to **geopoint) and that
  • the format string only has access to the provided map and not the entire scope of variables (compared to F-strings).
Tom
  • 749
  • 4
  • 16
2

Most answers formatted only the values of the dict.

If you want to also format the key into the string you can use dict.items():

geopoint = {'latitude':41.123,'longitude':71.091}
print("{} {}".format(*geopoint.items()))

Output:

('latitude', 41.123) ('longitude', 71.091)

If you want to format in an arbitry way, that is, not showing the key-values like tuples:

from functools import reduce
print("{} is {} and {} is {}".format(*reduce((lambda x, y: x + y), [list(item) for item in geopoint.items()])))

Output:

latitude is 41.123 and longitude is 71.091

victortv
  • 7,874
  • 2
  • 23
  • 27
  • note there's a chance 'longitude' could come before 'latitude' from `geopoint.items()` ;) – Tcll Apr 17 '20 at 22:20