Saturday, March 17, 2018

What the f in f!

Or nested f-strings in python 3.6

Someone wrote some code to format the printing of a list of floats, which got me thinking...
In their video they did a great little history of formatting options in Python.
They then went on to consider how to format a list of floats but to n-decimal places.

Their code was this:

In [4]:
l = [x**.5 for x in range(1, 11)]
print('Default:', l)
Default: [1.0, 1.4142135623730951, 1.7320508075688772, 2.0, 2.23606797749979, 2.449489742783178, 2.6457513110645907, 2.8284271247461903, 3.0, 3.1622776601683795]
In [5]:
s = ', '.join(map(lambda x: f"{x:.3f}", l))
print(f'Their floats: [{s}]')
Their floats: [1.000, 1.414, 1.732, 2.000, 2.236, 2.449, 2.646, 2.828, 3.000, 3.162]
I could easily see what they were doing but:
  1. I didn't like the mixing of the use of list comprehensions and map
  2. I immediatly thought: Could we do an f-string inside an f-string?

My nested f-string solution

A bit of thought produced this:
In [6]:
l = [x**.5 for x in range(1, 11)]
print(f'''floats: [{", ".join(f"{x:.3f}" for x in l)}]''')
floats: [1.000, 1.414, 1.732, 2.000, 2.236, 2.449, 2.646, 2.828, 3.000, 3.162]
The nest of the f-string does work! it isn't very clear code, but the f-string implementation is nestable.
Now the above uses an outer triple-quoted string to make the use of inner strings easier. It will work, however with all the same quotes but not in Jupyter notebook
Python 3.6.0a3 in idle gives this:
>>> print(f"floats: [{\", \".join(f\"{x:.3f}\" for x in l)}]")
floats: [1.000, 1.414, 1.732, 2.000, 2.236, 2.449, 2.646, 2.828, 3.000, 3.162]
>>> 

In Jupyter I get the following:
In [7]:
print(f"floats: [{\", \".join(f\"{x:.3f}\" for x in l)}]")
  File "", line 1
    print(f"floats: [{\", \".join(f\"{x:.3f}\" for x in l)}]")
         ^
SyntaxError: f-string expression part cannot include a backslash
I guess I need to report this...

My code with the nesting removed

(I prefer comprehensions over the use of map)
In [8]:
l = [x**.5 for x in range(1, 11)]
s = ", ".join(f"{x:.3f}" for x in l)
print(f'floats: [{s}]')
floats: [1.000, 1.414, 1.732, 2.000, 2.236, 2.449, 2.646, 2.828, 3.000, 3.162]