Computer Programming 1 (Python) • Strings & Text Processing

Formatted Reports — I can use f-strings to produce readable reports with aligned labels/values at my level.

Learn to craft polished, aligned text output using Python f-strings so your programs communicate data clearly.

A Python coding terminal with a program flow and laptop.
Inside this lesson 10 sections 5 questions 7 figures

SECTION 1

Historical Context & Motivation

From the earliest days of computing, programs needed to display results to human users. Raw data dumped to a screen is hard to read, so developers quickly realized they needed tools for string formatting — the process of arranging text, numbers, and labels into neat, aligned columns. Think of it like designing a receipt or a scoreboard: the data is only useful if people can scan it at a glance.

Python has gone through several generations of formatting tools. Each new approach aimed to make the code more readable and more powerful. Understanding this evolution helps you appreciate why f-strings (formatted string literals) are the modern standard and why they make your life so much easier.

1991 • Python 1.0 — The % Operator

Python adopted C-style formatting with the % operator, using placeholders like %s and %d. It worked but was error-prone and hard to read.

2008 • Python 2.6 — str.format()

The .format() method introduced curly-brace placeholders like "{}".format(value). This was clearer, but long expressions still felt clunky.

2015 • PEP 498 Proposed

Eric V. Smith proposed f-strings in PEP 498, arguing that embedding expressions directly inside strings would reduce boilerplate and improve readability.

2016 • Python 3.6 — f-strings Released

F-strings became available with Python 3.6. Developers could now write f"Hello, {name}!" and embed any valid Python expression inside the braces.

2023 • Python 3.12 — Nested f-strings

Python 3.12 lifted earlier restrictions, allowing fully nested f-strings and more complex expressions inside braces, making formatted reports even more flexible.

The core question that drove this evolution is simple: How do we turn raw data into a report that is easy for a human to read? F-strings answer that question with concise syntax and powerful alignment controls that we will explore throughout this lesson.

SECTION 2

Core Principles of F-String Formatting

Before diving into code, it helps to understand the foundational ideas behind f-string formatting. Every formatted report relies on the same handful of principles, regardless of whether you are printing a grade book, a sports roster, or a financial summary.

1

Interpolation

An f-string lets you embed a Python expression inside curly braces {}. Python evaluates the expression and inserts its value into the string at runtime.
2

Format Specifier

After the expression, a colon : introduces a mini-language that controls width, alignment, fill characters, and number precision — all in one compact instruction.
3

Alignment

Use < for left, > for right, and ^ for center alignment within a fixed-width field. This is how columns line up.
4

Width & Fill

A number after the alignment character sets the field width. An optional fill character (like a dot or dash) replaces spaces, letting you create visual guides between labels and values.
5

Type Codes

Letters like d (integer), f (float), s (string), and , (thousands separator) tell Python how to display the value.
✦

KEY TAKEAWAY

Think of an f-string format specifier like a label maker at the post office. You set the width of the label (field width), decide whether the text should be pushed left, right, or centered (alignment), and choose what fills the empty space (fill character). The label maker then stamps out perfectly uniform labels every time — and that is exactly what your code does for each row of a report.

SECTION 3

Anatomy of an F-String Format Specifier

The diagram below breaks down the complete syntax of a format specifier inside an f-string. Every element is optional, but understanding the full pattern gives you total control over your output.

Anatomy of an F-String Format Specifierf"{name:.<20s}"ExpressionAny valid Pythonexpression or variable: colonstarts format specFill CharacterReplaces paddingspaces (e.g., ".", "-")Alignment< left > right ^ centerWidthMinimum charactersType Codes=string d=int f=float,=thousands %=percent
Each colored segment maps to one part of the format specifier mini-language. The expression is evaluated first, then formatted according to the fill, alignment, width, and type code.

Reading the diagram from left to right, notice that the colon acts as a divider between the expression you want to display and the instructions for how to display it. Everything after the colon is optional — if you write just f"{name}", Python prints the value with no extra padding or formatting. But when you add a width like f"{name:20}", Python guarantees the output occupies at least 20 characters, padding with spaces on the right (for strings) or on the left (for numbers) by default.

SECTION 4

The Format Spec Mini-Language

Python's format specifier follows a compact pattern. While it is not a math equation in the traditional sense, you can think of it as a formula that Python parses left to right. The general syntax is:

FORMAT SPECIFIER SYNTAX

{ expression : [fill][align][sign][#][0][width][grouping][.precision][type] }

fill = any single character used for padding; align = < (left), > (right), ^ (center); width = minimum number of characters; .precision = decimal places for floats; type = d (integer), f (fixed-point float), s (string), (comma separator), % (percentage).

Alignment in Action

The three alignment operators are the key to building columnar reports. When you write f"{'Alice':<15}", Python takes the string Alice (5 characters) and pads it on the right with 10 spaces so it fills 15 characters total. If you switch to f"{'Alice':>15}", the padding goes on the left instead, pushing "Alice" to the right edge of the 15-character field.

PADDING CALCULATION

paddingchars=width−len(str(value))padding_chars = width − len(str(value))

If the value is already longer than the width, no truncation occurs — Python simply uses the full value. Padding only adds characters; it never removes them.

Number Formatting

For numbers, the format spec gets especially useful. Writing f"{price:.2f}" forces two decimal places, so 9.5 becomes 9.50. Adding a comma for thousands grouping — f"{population:,}" — turns 1000000 into 1,000,000. You can even combine them: f"{revenue:>12,.2f}" right-aligns a number in a 12-character field with commas and two decimal places.

COMMON NUMBER FORMAT EXAMPLES

f"{42:05d}" → 00042 f"{0.753:.1%}" → 75.3% f"{3.14159:.3f}" → 3.142

05d zero-pads an integer to 5 digits; .1% multiplies by 100 and shows 1 decimal place with a % sign; .3f rounds a float to 3 decimal places.

SECTION 5

Building Aligned Reports Column by Column

The real power of f-strings shines when you combine multiple format specifiers in a single line to build a row of a report. Each column has its own width and alignment, and because every row uses the same format string, the columns automatically line up. The diagram below shows how a three-column report is constructed.

Three-Column Report LayoutFormat string used for every row:f"{name:<20}{score:>8}{grade:^10}"Output (monospace view)← LEFT ALIGNED (20 chars) →RIGHT (8) →← CENTER (10) →Name Score Grade Alice 95 A Bob 87 B+ Charlie 72 C Diana 91 A- Left-aligned columnRight-aligned columnCenter-aligned column
The dashed vertical lines show column boundaries. Names are left-aligned so they start at the same position. Scores are right-aligned so the ones digits line up. Grades are center-aligned for a balanced look.

Notice how every row uses the exact same format string. You typically write the format string once — often inside a loop — and feed it different data each iteration. This pattern guarantees that no matter how many rows your report has, the columns remain perfectly aligned.

💡

Pro Tip: Header Rows

Use the same format string for your header row, but pass the column titles as the values. For example: f"{'Name':<20}{'Score':>8}{'Grade':^10}". Then print a separator line like print('-' * 38) right after the header to visually separate it from the data.

SECTION 6

Worked Example: Student Grade Report

Let's build a complete, formatted grade report step by step. We'll start with raw data and end with a polished, print-ready output.

Building a Student Grade Report

  1. 1

    Step 1 — Define the Data

    Create a list of tuples, where each tuple holds a student's name, score, and letter grade. For example: students = [ ("Alice Johnson", 95, "A"), ("Bob Martinez", 87, "B+"), ("Charlie Kim", 72, "C"), ("Diana Patel", 91, "A-"), ]
    Data stored as a list of 4 tuples.
  2. 2

    Step 2 — Design the Format String

    Decide on column widths. The longest name is "Alice Johnson" (13 characters), so we choose 20 for breathing room. Scores are at most 3 digits, so width 8 gives comfortable right-alignment. Grades are short, so 10 centered works well. The format string is: row_fmt = f"{name:<20}{score:>8}{grade:^10}" But we will build this inside a loop, so we use the variables from unpacking each tuple.
    Format: left 20, right 8, center 10 = 38 total characters per row.
  3. 3

    Step 3 — Print the Header and Separator

    Use the same alignment on string literals to create a matching header row, then print a line of dashes: print(f"{'Name':<20}{'Score':>8}{'Grade':^10}") print('-' * 38)
    Name Score Grade --------------------------------------
  4. 4

    Step 4 — Loop Through the Data

    Iterate over the list, unpack each tuple, and print the formatted row: for name, score, grade in students: print(f"{name:<20}{score:>8}{grade:^10}")
    Alice Johnson 95 A Bob Martinez 87 B+ Charlie Kim 72 C Diana Patel 91 A-
  5. 5

    Step 5 — Add a Summary Footer

    Calculate the average score and display it with two decimal places, right-aligned under the Score column: avg = sum(s for _, s, _ in students) / len(students) print('-' * 38) print(f"{'Average':<20}{avg:>8.2f}")
    -------------------------------------- Average 86.25
✦

KEY TAKEAWAY

The secret to a clean report is reusing the same format string for every row — header included. Think of it like a spreadsheet template: you set the column widths once, then every row of data snaps into place automatically. If you later add more students, the alignment stays perfect.

SECTION 7

F-Strings vs. Other Formatting Methods

You may encounter older Python code that uses the % operator or the .format() method. Here is how these three approaches compare when producing the same output.

Feature% Operator.format()f-strings
Introduced inPython 1.0 (1991)Python 2.6 (2008)Python 3.6 (2016)
ReadabilityLow — placeholders are far from valuesMedium — clearer but verboseHigh — values inline
Expressions inline?NoLimitedYes — any expression
PerformanceModerateModerateFastest — compiled at parse time
Alignment syntax%-20s, %8d{:<20}, {:>8}{name:<20}, {score:>8}
Best for new code?No — legacy onlyRare edge casesYes — recommended

Comparison of Python string formatting approaches

✦

KEY TAKEAWAY

If you are writing new Python code (Python 3.6+), f-strings should be your default. They run faster, they read more naturally because values appear right where they are used, and they support the full format spec mini-language. You only need the older methods when maintaining legacy code.

SECTION 8

Connecting to Advanced Formatting

The f-string skills you are building now are the foundation for more advanced output scenarios. As your programs grow, you will encounter situations that push beyond simple console reports — writing data to files, generating HTML tables, or creating PDF reports. The table below shows how the concepts you have learned map to these next-level tools.

This Lesson (F-Strings)Advanced Tools
Column alignment with <, >, ^Libraries like tabulate or PrettyTable auto-align columns and add borders
Number precision with .2fThe locale module formats currencies by country (e.g., $1,234.56 vs. 1.234,56 €)
Printing to console with print()Writing to .txt or .csv files with open() and the csv module
String concatenation in f-stringsTemplate engines like Jinja2 build entire web pages from data
Thousands separator with , groupingpandas DataFrame.to_string() for formatted data analysis output

From f-strings to advanced output tools

Every one of these advanced tools still relies on the same core idea: specify the field width, choose an alignment, and format the value. Mastering f-string formatting now means you already understand the underlying logic; the advanced libraries just wrap it in extra convenience features.

ℹ

Looking Ahead

In future courses, you might explore Python's logging module, which uses format strings to record events in running applications. You will also see f-strings used extensively in web frameworks like Flask and Django, where dynamic text generation is central to building interactive websites.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain, in your own words, the difference between f"{value:<10}" and f"{value:>10}". Why would you use left-alignment for text labels but right-alignment for numbers in a report?
Show answer
The < operator left-aligns the value within a 10-character field, padding spaces on the right. The > operator right-aligns it, padding spaces on the left. Text labels are left-aligned because our eyes naturally scan text from left to right, making the starting position consistent. Numbers are right-aligned so that the ones, tens, and hundreds digits stack vertically, making it easy to compare magnitudes at a glance — similar to how you line up numbers in a math class before adding them.

Question 2: BASIC CALCULATION

Write a single f-string expression that displays the variable price = 49.99 right-aligned in a field of 12 characters with a thousands comma and exactly two decimal places. What would the output look like if price = 1234567.8?
Show answer
The f-string is f"{price:>12,.2f}". For price = 49.99, the output is 49.99 (7 leading spaces + 5 characters for the number). For price = 1234567.8, the output is 1,234,567.80 — exactly 12 characters with commas and two decimals. The comma grouping and .2f precision work together; note that the field expands beyond 12 when the formatted number is already that long.

Question 3: INTERMEDIATE

You have a list of items for a store receipt: items = [("Widget", 3, 4.99), ("Gizmo", 1, 29.95), ("Thingamajig", 12, 0.50)] Each tuple is (item_name, quantity, unit_price). Write a complete Python script that prints a formatted receipt with four columns: Item (left-aligned, 18 chars), Qty (right-aligned, 5 chars), Unit Price (right-aligned, 10 chars with 2 decimals), and Total (right-aligned, 10 chars with 2 decimals). Include a header, separator line, and a grand total row at the bottom.
Show answer
A complete solution: items = [("Widget", 3, 4.99), ("Gizmo", 1, 29.95), ("Thingamajig", 12, 0.50)] print(f"{'Item':<18}{'Qty':>5}{'Unit $':>10}{'Total':>10}") print('-' * 43) grand = 0 for name, qty, price in items: total = qty * price grand += total print(f"{name:<18}{qty:>5}{price:>10.2f}{total:>10.2f}") print('-' * 43) print(f"{'Grand Total':<23}{'':>10}{grand:>10.2f}") The key decisions are: choosing column widths that accommodate the longest item name plus some padding, right-aligning all numeric columns, using .2f for currency formatting, and accumulating the grand total in a variable during the loop. The separator lines visually frame the data.

Question 4: APPLIED

A teacher stores test scores in a dictionary: scores = {"Alice": [88, 92, 79], "Bob": [65, 70, 72], "Carla": [95, 98, 100]} Write a program that prints a report with columns: Student (left-aligned, 15 chars), Test 1 (right-aligned, 8 chars), Test 2 (right-aligned, 8 chars), Test 3 (right-aligned, 8 chars), and Average (right-aligned, 10 chars with 1 decimal). Use a dot-fill character for the Student column so the output looks like Alice.......... 88.
Show answer
Here is one approach: scores = {"Alice": [88, 92, 79], "Bob": [65, 70, 72], "Carla": [95, 98, 100]} print(f"{'Student':.<15}{'Test 1':>8}{'Test 2':>8}{'Test 3':>8}{'Average':>10}") print('-' * 49) for name, tests in scores.items(): avg = sum(tests) / len(tests) print(f"{name:.<15}{tests[0]:>8}{tests[1]:>8}{tests[2]:>8}{avg:>10.1f}") The dot-fill is achieved by placing a period before the alignment character: :.<15. This replaces the default space padding with dots, creating a visual leader line between the name and the numbers. The average is computed using sum() and len(), and displayed with one decimal place using .1f. This technique is commonly seen in tables of contents and financial statements.

Question 5: CRITICAL THINKING

Suppose your dataset contains a name that is 25 characters long, but your format string specifies a width of 15 for the name column. What happens to the alignment of the rest of the row? Propose two different strategies to handle this problem, and write example code for each. Discuss the trade-offs.
Show answer
Python does not truncate values — a 25-character name overflows the 15-character field, pushing subsequent columns to the right and breaking alignment for that row. Strategy 1: Truncate with slicing. Use f"{name[:15]:<15}" to clip the name to 15 characters. This guarantees alignment but loses information. Strategy 2: Dynamically compute the width. Before printing, find the longest name with max_len = max(len(n) for n in names) and use that as the width: f"{name:<{max_len}}" (note the nested braces — the width itself is a variable). This preserves all data and maintains alignment, but the report may become very wide if one name is exceptionally long. The best choice depends on context: truncation works for fixed-width displays (like a terminal), while dynamic widths work for reports that can scroll horizontally or wrap to new pages.

SUMMARY

Lesson Summary

Summary

Python f-strings (introduced in Python 3.6) let you embed expressions directly in strings using curly braces. After a colon, you enter the format specifier mini-language, where you control fill characters, alignment (< left, > right, ^ center), field width, precision for decimals, and type codes like d, f, and ,.

To build a formatted report, design one format string that defines every column's width and alignment, then reuse it for the header and every data row inside a loop. Left-align text labels and right-align numbers so digits stack vertically. Add separator lines with string multiplication ('-' * width) and summary rows for totals or averages. These skills transfer directly to advanced tools like tabulate, pandas, and web template engines.

Varsity Tutors • Computer Programming 1 (Python) • Formatted Reports — I can use f-strings to produce readable reports with aligned labels/values at my level.