Pregunta

I want to format and print data from a dictionary with the keyword unpacking operator **.

The format string possibly refers to many keywords and the dictionary may not have all the required keys. For the missing keys, I would like to use the string 'N/A' as a default value.

I would like a clever solution for this default-ing problem, possibly using a defaultdict.

Here is a first solution using a regular dict.

# Cumbersome solution with dict
format_str = '{date} {version} {comment}'
data       = dict()

data['date']    = 'today'
data['version'] = 'v1'
data['comment'] = 'N/A'  # I want to avoid this

print format_str.format(**data)
# prints: today v1 N/A

I would like to avoid the explicit assignment of 'N/A' for missing keys.

The following solution also uses a regular dict and analyzes the format string to build the list of keywords:

# Another solutions with handmade defaults for missing keys
format_str = '{date} {version} {comment}'
data       = dict()

data['date']    = 'today'
data['version'] = 'v1'

import re
for k in re.findall('{(\w+)}', format_str):
    if k not in data:
        data[k] = 'N/A'

print format_str.format(**data)
# prints: today v1 N/A

This solution with re.findall is not really elegant nor robust since the syntax of format string is much more complex than the above {(\w+)}.

The following solution would be my favorite one… if it did not fail for an obvious reason.

# Failed attempt with defaultdict
from collections import defaultdict
format_str = '{date} {version} {comment}'
data       = defaultdict(lambda x:'N/A')

data['date']    = 'today'
data['version'] = 'v1'

print format_str.format(**data)
# raises KeyError: 'comment'

The problem here is that ** actually unpacks the keywords in data, so format does not search data for the requested keywords and thus does not give data the opportunity to provide its default value.

Is there a solution to this ? Eg is there an alternative format function which would actually call data.get(kwd) and thus get the N/As?

¿Fue útil?

Solución

Use string.Formatter.vformat() and pass the defaultdict to that.

from collections import defaultdict
from string import Formatter

fmtr = Formatter()
format_str = '{date} {version} {comment}'
data = defaultdict(lambda: 'N/A')

data['date'] = 'today'
data['version'] = 'v1'

print fmtr.vformat(format_str, (), data)

Otros consejos

Good solution using formatter is already given

However you are not benefiting from keyword unpacking in this case. You are forced to specify keys you wanted to print in the format string.

format_str = '{date} {version} {comment}'

And dict as intended already maintains the Keyword-Value mapping

so for dict like this

>>> d
{'version': 'v1', 'date': 'today'}

using default value to dict.get()

>>> print '{} {} {}'.format(d['date'],d['version'],d.get('comment','n/a'))
today v1 n/a

if you want Key Word management in one place (as you did with format string), I would try something like this

>>> f=lambda x: (x.get('date'),x.get('version'),x.get('comment','N/A'))
>>> print '{} {} {}'.format(*f(d))
today v1 N/A

or this

>>> f=lambda x: '{} {} {}'.format(x['date'],x['version'],x.get('comment','N/A'))
>>> print f(d)
today v1 N/A

the benefit is, Less imports

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top