Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get Python to gracefully format None and non-existing fields [duplicate]

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.

like image 727
Juan A. Navarro Avatar asked Nov 27 '13 16:11

Juan A. Navarro


People also ask

What are Formatters in Python?

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.

How do you escape a format in Python?

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.


2 Answers

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.

like image 118
dawg Avatar answered Sep 18 '22 19:09

dawg


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' 
like image 34
Simeon Visser Avatar answered Sep 19 '22 19:09

Simeon Visser