If I write in Python:
data = {'n': 3, 'k': 3.141594, 'p': {'a': 7, 'b': 8}} print('{n}, {k:.2f}, {p[a]}, {p[b]}'.format(**data)) del data['k'] data['p']['b'] = None print('{n}, {k:.2f}, {p[a]}, {p[b]}'.format(**data))
I get:
3, 3.14, 7, 8 Traceback (most recent call last): File "./funky.py", line 186, in <module> print('{n}, {k:.2f}, {p[a]}, {p[b]}'.format(**data)) KeyError: 'k'
Instead of an error message, how can I get Python to more gracefully format the None's and non existent fields?
To give an example, I would like to see in the output something more like:
3, 3.14, 7, 8 3, ~, 7, ~
Ideally, of course, I would like to be able to specify the string used instead of those missing values.
Formatters work by putting in one or more replacement fields and placeholders defined by a pair of curly braces { } into a string and calling the str. format(). The value we wish to put into the placeholders and concatenate with the string passed as parameters into the format function.
You can use the backslash (\) escape character to add single or double quotation marks in the python string format. The \n escape sequence is used to insert a new line without hitting the enter or return key.
The recommendation in PEP 3101 is to subclass Formatter:
import string class PartialFormatter(string.Formatter): def __init__(self, missing='~~', bad_fmt='!!'): self.missing, self.bad_fmt=missing, bad_fmt def get_field(self, field_name, args, kwargs): # Handle a key not found try: val=super(PartialFormatter, self).get_field(field_name, args, kwargs) # Python 3, 'super().get_field(field_name, args, kwargs)' works except (KeyError, AttributeError): val=None,field_name return val def format_field(self, value, spec): # handle an invalid format if value==None: return self.missing try: return super(PartialFormatter, self).format_field(value, spec) except ValueError: if self.bad_fmt is not None: return self.bad_fmt else: raise fmt=PartialFormatter() data = {'n': 3, 'k': 3.141594, 'p': {'a': '7', 'b': 8}} print(fmt.format('{n}, {k:.2f}, {p[a]}, {p[b]}', **data)) # 3, 3.14, 7, 8 del data['k'] data['p']['b'] = None print(fmt.format('{n}, {k:.2f}, {p[a]:.2f}, {p[b]}', **data)) # 3, ~~, !!, ~~
As set up, it will print ~~
if a field or attribute is not found and !!
if an invalid format is used given the field value. (Just use None
for the keyword argument bad_fmt
if you want the default of a value error raised.)
To handle missing keys, you need to subclass both get_field
to catch the KeyError
or AttributeError
and format_field
to return a default value for the missing key.
Since you are catching format_field
errors, you can catch a bad format field as well by catching the ValueError
from the superclass.
If you're able to do the formatting separately you could use Template.safe_substitute
which gracefully handles missing values:
>>> from string import Template >>> t = Template("$a $b $c") >>> t.safe_substitute(a=3) '3 $b $c'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With