Here is the same total printed two ways. One looks like a machine dumped it; the other looks like a receipt.
Total:160.5 Total 160.50
The numbers are identical. Only the formatting changed. This part is about text in Python: how strings work, the handful of things you do to them most, and the tool that makes output look deliberate instead of accidental. That tool is the f-string, and by the end of this part your expense tracker prints a clean receipt.
f in front of the quotes and drop any variable inside curly braces. Add a colon and a format code to control decimals, width, and alignment.This part assumes Part 9, where you wrapped your add and print steps in functions. You need Python 3 and about twenty five minutes. Every snippet runs as printed. If you type along, you will reproduce each result exactly.
Strings, and the quotes around them
A string is a run of characters: letters, digits, spaces, punctuation. You write one by wrapping text in quotes. Single or double both work, and they behave identically:
name = 'Asha' city = "Pune" print(name, city)
Asha Pune
The reason both exist is so you can put one kind of quote inside the other without a fight. If your text contains an apostrophe, wrap it in double quotes:
note = "it's lunch" print(note)
it's lunch
Picking single or double
Pick one and stay consistent. This series uses single quotes, and switches to double only when the text itself contains an apostrophe. There is no speed or correctness difference. Consistency is the whole point, because mixed quoting is one more thing for your eyes to trip on when you scan code later.
Things you can do to a string
Strings come with built-in tools called methods. You call a method by putting a dot and its name after the string. Each one returns a new string and leaves the original untouched, which surprises beginners: a string never changes in place.
raw = ' Groceries '
clean = raw.strip()
print(clean)
print(clean.upper())
print(clean.replace('c', 'C'))Groceries GROCERIES GroCeries
You can also measure a string with len() and pull out pieces by position. Positions start at zero, not one, which is the second thing that trips beginners.
word = 'python' print(len(word)) print(word[0]) print(word[0:3])
6 p pyt
| You want to | Method or tool | Example result |
|---|---|---|
| Remove edge spaces | .strip() | ‘ hi ‘ becomes ‘hi’ |
| Upper or lower case | .upper() / .lower() | ‘Hi’ becomes ‘HI’ |
| Swap text | .replace(a, b) | ‘cat’ becomes ‘car’ |
| Count characters | len(s) | ‘python’ gives 6 |
| Split on a separator | .split(',') | ‘a,b’ becomes [‘a’, ‘b’] |
Table 1. The string tools you will reach for most in your first months.
f-strings: putting values inside text
You often want to build a sentence out of values you already have. The old way was gluing pieces with plus signs, and it forced you to convert numbers to text by hand:
amount = 120.5 line = 'Spent ' + str(amount) + ' today' print(line)
Spent 120.5 today
The f-string does the same thing without the noise. Put an f before the opening quote, then drop any value inside curly braces. Python fills each brace with the value:
amount = 120.5
line = f'Spent {amount} today'
print(line)Spent 120.5 today
Figure 1. The braces mark a slot. Python replaces the slot with the value.
Controlling how a number looks
The real power shows up when you add a colon and a format code after the value. This is where a bare number becomes money. A few codes cover almost everything you will need:
price = 1250.5
print(f'{price:.2f}')
print(f'{price:,.2f}')
print(f'{price:>10.2f}')1250.50 1,250.50 1250.50
| Format code | What it does | 1250.5 becomes |
|---|---|---|
:.2f | Two decimal places | 1250.50 |
:,.2f | Thousands separator plus decimals | 1,250.50 |
:>10 | Right align in a width of 10 | right padded |
:<10 | Left align in a width of 10 | left padded |
:^10 | Center in a width of 10 | centered |
Table 2. The format codes that turn raw numbers into tidy columns.
f before the quotes, Python does not fill the braces. It prints them literally, and there is no error to warn you:
Spent {amount} todayf. Check the front of the string first.Splitting text apart and joining it back
Real data often arrives as one line of text with separators, and you need the pieces out of it. Two methods handle almost all of that work. .split() breaks a string into a list on a separator, and .join() glues a list back into a single string. They are opposites, and together they cover most day to day text handling.
row = 'food,120.5,lunch'
parts = row.split(',')
print(parts)
print(parts[0])
print(parts[1])['food', '120.5', 'lunch'] food 120.5
Notice the amount came back as text, not a number. Splitting always yields strings, so you convert before doing any maths on them. That is a real trap, and Part 12 handles it head on.
Going the other way, .join() takes a list and glues it with the separator you call it on:
fields = ['travel', '40', 'cab'] line = ','.join(fields) print(line)
travel,40,cab
One catch trips people the first time. .join() only works on a list of strings. Hand it a number and Python stops with a clear last line:
TypeError: sequence item 1: expected str instance, int found
Read that last line the way Part 2 taught. It wanted a string and found an int, so convert numbers with str() before joining. This split and join pair is exactly how a program reads and writes simple data files, which is where the next part goes.
.split() it into fields, and the last thing before saving is .join() them back. Getting fluent with this pair now makes the file work in Part 11 feel like something you have already done.When formatting goes too far
f-strings let you put whole expressions inside the braces, and that is where people overreach. You can write a brace that does a calculation, calls a function, and formats the result all at once. You should usually not. Here is the kind of line that looks clever and reads badly:
print(f'{sum(x["amt"] for x in items):,.2f}')Pull the work out into a named step, then format the plain result. The two line version is longer and much easier to trust:
total = sum(x['amt'] for x in items)
print(f'{total:,.2f}')My rule, and it goes against the tidy one liner advice you will see: a brace should hold a value or a simple attribute, not a computation. If you need a calculation, give it a name on its own line first. You are not being paid by the fewest lines. You are paid to write code the next person can read, and that person is often you in three months.
A tidy receipt for the tracker
Your expense tracker can now print output that looks intentional. Building on the functions from Part 9, here is a print_receipt that lines up categories on the left and amounts on the right, then totals them. Every value is placed with an f-string and a format code.
expenses = [
{'amount': 120.5, 'category': 'food'},
{'amount': 40, 'category': 'travel'},
]
def print_receipt(items):
cat_head = 'Category'
amt_head = 'Amount'
print(f'{cat_head:<12}{amt_head:>10}')
print('-' * 22)
total = 0
for item in items:
cat = item['category']
amt = item['amount']
print(f'{cat:<12}{amt:>10.2f}')
total += amt
print('-' * 22)
label = 'Total'
print(f'{label:<12}{total:>10.2f}')
print_receipt(expenses)Category Amount ---------------------- food 120.50 travel 40.00 ---------------------- Total 160.50
The <12 pads each category to a width of twelve on the left, and the >10.2f right aligns each amount in a width of ten with two decimals, so the numbers stack in a clean column. This is the same skill every report, invoice, and log line in real software depends on.
:.2f, as in a format like f'{price:.2f}'. A strong follow up is mentioning :,.2f for a thousands separator. It is a small question that quickly shows whether you have actually formatted output or only ever printed raw values.item = 'coffee' and price = 4.5. Print one line that reads exactly coffee costs 4.50, with the price shown to two decimal places. Expected output:
coffee costs 4.50
Show solution
item = 'coffee'
price = 4.5
print(f'{item} costs {price:.2f}')Two slots in one f-string. The first drops in the text as is, the second formats the number to two decimals with :.2f.
String questions people actually ask
Why did my string not change after I called .upper()? Because string methods return a new string and never edit the original. Capture the result: name = name.upper(). If you throw the result away, nothing seems to happen.
What is the difference between an f-string and .format()? They do the same job. The f-string reads better because the value sits right where it appears in the text. Older code uses .format(); new code should default to f-strings.
Can I put an f-string across several lines? Yes. Wrap it in triple quotes and it keeps your line breaks, or use n inside a normal f-string to force a new line where you want one.
How do I show a real curly brace in an f-string? Double it. {{ prints one {, and }} prints one }. This comes up when you build things like JSON by hand.
Is there a quick way to print a variable name and value while debugging? Yes, and it is genuinely handy: f'{price=}' prints price=4.5. The equals sign inside the braces shows both the name and the value.
References
Input and output, formatted (official tutorial) · Formatted string literals (language reference) · Format specification mini-language


DrJha