7

Is there a way to use a variable to decide the number of decimal points in literal string interpolation?

for example if I have something like

f'{some_float:.3f}'

is there a way to replace the 3 with a variable?

The end goal is to add data labels to a bar chart:

def autolabel_bar(rects, ax, decimals=3):
    """
    Attach a text label above each bar displaying its height
    """
    for rect in rects:
        height = rect.get_height()
        ax.text(rect.get_x() + rect.get_width()/2.,
                height + 0.035,
                f'{round(height,decimals):.3f}',
                ha='center',
                va='center')

But I can't think of an easy way to replace the 3 in the string interpolation with the variable decimal.

Dan
  • 45,079
  • 17
  • 88
  • 157
  • The only way I can see this working is with the use of RegEx. – Adriano Nov 28 '17 at 09:51
  • 1
    Without string interpolation `f"..."` you could use `"{:.{}f}".format(some_float, decimals)`, but I guess you know that. Not sure if the same works here, too: `f"{height:.{decimals}f}"` (I don't have 3.6 right now) – tobias_k Nov 28 '17 at 09:54
  • @tobias_k I did not know that and it turns out `f"{some_float:.{decimal}f}"` works just fine. Perhaps add it as an aswer? – Dan Nov 28 '17 at 09:59

2 Answers2

15

Format specifiers can be nested. In Python 3.5, this would look e.g. like this:

"{:.{}f}".format(some_float, decimals)

But it turns out the same also works with Python 3.6 f"..." format strings.

>>> some_float = math.pi
>>> decimals = 3
>>> f"{some_float:.{decimals}f}"
'3.142'

Also works in combination with round:

>>> decimals = 5
>>> f"{round(math.pi, decimals):.{decimals}f}"
'3.14159'
tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • lol...and a mod deleted by last comment about how an answer can get marked and accepted within 48secs!! :D – NoobEditor Nov 28 '17 at 10:29
  • @NoobEditor Don't know if there should be any guards in place to prevent this, but I proposed the solution in comments, as I did not have Python 3.6 running, so OP already checked it before I even posted the answer. – tobias_k Nov 28 '17 at 10:31
  • nothing in bad spirit buddy, my comment was light hearted.Was just surprised to see someone deleting it! :) – NoobEditor Nov 28 '17 at 10:38
2

Yes, you can escape string templating literals with double curly braces:

decimals = 5
template = f'{{some_float:{decimals}.f}}'
// '{some_float:5.f}'
template.format(some_float=some_float)

I don't think you can use formatted string literals for the second substitution, but I think it's a nice solution anyway.

I think you made a mistake in the first code example in your question, the dot is in a wrong place in your formatter. (3.f instead of .3f)

fodma1
  • 3,485
  • 1
  • 29
  • 49
  • @tobias_k you're right, that works too, and your proposed solution is better in my opinion. I'd still leave my answer since it can serve a different use case when someone really wants to assemble the formatter. – fodma1 Nov 28 '17 at 10:02
  • Yes it was a mistake, I've corrected it. Turn out `f"{height:.{decimals}f}"` works just fine as well (I'm running '3.6.1 |Anaconda custom (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]') – Dan Nov 28 '17 at 10:04