11

Is there any way to format string with dict but optionally without key errors?

This works fine:

opening_line = '%(greetings)s  %(name)s !!!'
opening_line % {'greetings': 'hello', 'name': 'john'}

But let's say I don't know the name, and I would like to format above line only for 'greetings'. Something like,

 opening_line % {'greetings': 'hello'}

Output would be fine even if:

'hii %(name)s !!!'  # keeping name un-formatted 

But this gives KeyError while unpacking

Is there any way?

dreftymac
  • 31,404
  • 26
  • 119
  • 182
Nikhil Rupanawar
  • 4,061
  • 10
  • 35
  • 51
  • **See also:** [py str.format missing values](https://stackoverflow.com/questions/20248355/how-to-get-python-to-gracefully-format-none-and-non-existing-fields) – dreftymac Oct 29 '17 at 09:56

5 Answers5

12

Use defaultdict, this will allow you to specify a default value for keys which don't exist in the dictionary. For example:

>>> from collections import defaultdict
>>> d = defaultdict(lambda: 'UNKNOWN')
>>> d.update({'greetings': 'hello'})
>>> '%(greetings)s  %(name)s !!!' % d
'hello  UNKNOWN !!!'
>>> 
piokuc
  • 25,594
  • 11
  • 72
  • 102
3

Some alternates to defaultDict,

greeting_dict = {'greetings': 'hello'}

if 'name' in greeting_dict :
    opening_line = '{greetings} {name}'.format(**greeting_dict)
else:
    opening_line = '{greetings}'.format(**greeting_dict)

print opening_line

Maybe even more succinctly, use dictionary get to set per parameter defaults,

'{greetings} {name}'.format(greetings=greeting_dict.get('greetings','hi'),
                            name=greeting_dict.get('name',''))
William Denman
  • 3,046
  • 32
  • 34
  • I'd extract commonality in `if/else` and instead, `fmt = '{greetings} {name}' if 'name' in greeting_dict else '{greetings}'; print(fmt.format(**greeting_dict))`. – Jason R. Coombs Apr 19 '16 at 19:13
2

For the record:

info = {
    'greetings':'DEFAULT',
    'name':'DEFAULT',
    }
opening_line = '{greetings} {name} !!!'

info['greetings'] = 'Hii'
print opening_line.format(**info)
# Hii DEFAULT !!!
feqwix
  • 1,362
  • 14
  • 16
0

I faced the same problem as yours and decided to create a library to solve this problem: pyformatting.
Here is the solution to your problem with pyformatting:

>>> from pyformatting import optional_format
>>> opening_line = '{greetings}  {name} !!!'
>>> optional_format(opening_line, greetings='hii')
'hii  {name} !!!'

The only problem is pyformatting doesn't support python 2. pyformatting supports python 3.1+ If i see any feedback on the need for 2.7 support i think i will add that support.

0dminnimda
  • 1,242
  • 3
  • 12
  • 29
0

You could subclass UserDict and customize __missing__ to your liking with .format_map():

from collections import UserDict


class FormatMapper(UserDict):
    def __missing__(self, key):
        return f'{key=} is MISSING'


info = FormatMapper({'greetings': 'hello', 'not_name': 'john'})
opening_line = '{greetings}  {name} !!!'

print(opening_line.format_map(info))

Output:

hello  key='name' is MISSING !!!
Terry Spotts
  • 3,527
  • 1
  • 8
  • 21