AW Dev Rethought

✂️ Perfection is achieved not when there is nothing more to add, but when there is nothing left to take away - Antoine de Saint-Exupéry

🐍 Python Everyday — ✨ F-Strings: From Messy Output to Clean Reports


Description:

A useful script should produce output that is easy to read. Python f-strings let us insert values into text and control their presentation. In this episode, three formatting choices turn sample folder sizes into a small report.


1. Start with the raw values

rows = [("Images", 12345.678), ("Logs", 987.6), ("Cache", 42.0)]
for name, size in rows:
    print(name, size)

Output:

Images 12345.678
Logs 987.6
Cache 42.0

The values are valid, but their decimal places and starting positions vary. These are illustrative sizes in kilobytes.


2. Set decimal precision and grouping

size = 12345.678
print(f"{size:.2f}")   # 12345.68
print(f"{size:,.2f}")  # 12,345.68

The colon introduces a format specification. Here, .2f displays two digits after the decimal point, and , groups the integer part into thousands. The result is rounded display text; size remains unchanged. Comma grouping is explicit, not locale-aware. Python format specification documentation.


3. Align the fields

for name, size in rows:
    print(f"{name:<10} {size:>12,.2f}")

Read the two fields as:

Field Meaning
{name:<10} Left-align the name in a minimum width of 10
{size:>12,.2f} Right-align the formatted number in a minimum width of 12, with grouping and two decimal places

Numeric width includes the digits, comma and decimal point. The space between fields separates the columns. These alignment rules are defined in Python's format mini-language.


4. Print the complete report

print(f"{'Folder':<10} {'Size (KB)':>12}")
print("-" * 23)
for name, size in rows:
    print(f"{name:<10} {size:>12,.2f}")
Folder        Size (KB)
-----------------------
Images        12,345.68
Logs             987.60
Cache             42.00

View the output in a monospaced font. The accompanying commented Python script builds this report and includes assertions for the examples. It runs with Python 3.6+ and needs no external packages.


5. Remember the boundaries

Width is a minimum, not a truncation rule: a name longer than ten characters will extend beyond that field. For larger reports, choose widths based on the actual content. This example uses simple ASCII labels; wide Unicode characters may need special handling for terminal alignment.

Formatting controls presentation. It does not change the stored value or fix floating-point arithmetic. Keep calculations numeric and format at the point where you display results.


Code Snippet:

# 1. Sample data: labels and illustrative sizes in kilobytes.
ROWS = [("Images", 12345.678), ("Logs", 987.6), ("Cache", 42.0)]


def build_report(rows):
    """Return a small, plain-text report for a monospaced display."""
    # <10: left-align in a minimum width of 10 characters.
    # >12: right-align in a minimum width of 12 characters.
    lines = [f"{'Folder':<10} {'Size (KB)':>12}", "-" * 23]
    for name, size in rows:
        # , groups thousands; .2f displays two digits after the decimal point.
        lines.append(f"{name:<10} {size:>12,.2f}")
    return "\n".join(lines)


def main():
    # 2. Raw output has uneven columns and decimal precision.
    print("BEFORE")
    for name, size in ROWS:
        print(name, size)

    # 3. A colon introduces formatting rules inside an f-string field.
    size = ROWS[0][1]
    print("\nONE VALUE, STEP BY STEP")
    print(f"Raw:        {size}")       # 12345.678
    print(f"Two places: {size:.2f}")   # 12345.68
    print(f"Grouped:    {size:,.2f}")  # 12,345.68

    # 4. Combine text alignment and numeric formatting into a report.
    print("\nAFTER")
    print(build_report(ROWS))

    # 5. Formatting returns text; the original numeric value is unchanged.
    formatted = f"{size:,.2f}"
    assert formatted == "12,345.68"
    assert size == 12345.678
    assert f"{42.0:,.2f}" == "42.00"
    assert f"{-1234.5:,.2f}" == "-1,234.50"
    assert build_report(ROWS).splitlines()[2] == "Images        12,345.68"

    # Width is a MINIMUM, not a limit: longer content is not truncated.
    assert f"{'VeryLongFolder':<10}" == "VeryLongFolder"
    # Fixed widths suit this small ASCII example; use wider/dynamic fields
    # for longer values. Comma grouping here is not locale-aware.


if __name__ == "__main__":
    main()

Link copied!

Comments

Add Your Comment

Comment Added!