We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Pattern Matching, f-Strings and Newer Syntax step 4 of 23
Format Table Rows and Money Amounts
This exercise puts the last two together. You write a row renderer that
takes its alignment and width from data, as in "Line Up Receipt Columns with
f-Strings". You also write a Money class with its own format codes, as in
"Give a Class Its Own Format Codes".
render_row([["Widget", "<"], ["12", ">"]], [10, 5]) # 'Widget | 12'
m = Money(Decimal("-1234.5"), "USD")
f"{m}" # 'USD -1,234.50'
f"{m:plain}" # '-1234.50'
f"{m:accounting}" # '(USD 1,234.50)'
f"{m:.3f}" # '-1234.500'
Render a row
Fill in render_row(cells, widths):
- Each cell is a pair
[text, align], wherealignis<,>or^. Each width is a whole number, zero or more. - Format each cell with one f-string that takes its alignment and width
from nested braces, as in
f"{text:{align}{width}}". Don't pad by hand or callljustorrjust. - Join the formatted cells with
"|". - A width is a minimum. Text longer than its width stays whole.
- Pair cells with widths using
zip(cells, widths, strict=True), so lists of different lengths raiseValueError. - Empty
cellsandwidthsgive"".
Give Money its format codes
Money holds a Decimal amount and a currency label, which is always
"USD" in the tests. Fill in __format__ so it handles each format spec
like this:
""is whatf"{m}"passes. It gives the currency, a space, and the amount with comma grouping and two decimal places."plain"gives the amount with two decimal places, without the currency or grouping."accounting"gives the currency and the amount without its sign, grouped, with two decimal places. If the amount is below zero, wrap the whole result in parentheses.- A spec that starts with
.and ends withf, such as.3f, goes to theDecimalwithformat(self.amount, format_spec). A malformed one, such as.xf, raisesValueErrorthere. - Any other spec raises
ValueError.
The / in __format__(self, format_spec: str, /) means callers must pass
the spec by position, as Python does when it calls __format__. A later
exercise in this track, on positional-only and keyword-only parameters,
covers / fully.
Decimal edge cases
- Get the amount without its sign from
self.amount.copy_abs().abs()can round the value to the decimal context's precision, 28 digits by default, andcopy_abs()never rounds. - Decide on the parentheses from the stored amount, not from the rounded
text.
-0.001rounds to0.00but is below zero, so it gives"(USD 0.00)". Decimal("-0")is negative zero. It is not below zero, so"accounting"gives"USD 0.00". The other specs keep its minus sign.
Write solve
solve(cells, widths, amounts, spec) returns a dictionary with the keys
"row", "money" and "error":
- Render the row first, outside any
try, and let a row error propagate. Only money formatting errors go in"error". - Turn each amount into
Money(Decimal(amount), "USD"), and format each one withformat(money, spec). - On success, return
{"row": row, "money": [...], "error": ""}, with the formatted amounts in order. - If formatting raises
ValueError, return{"row": row, "money": [], "error": "ValueError"}. Drop any amounts you already formatted. - An empty
amountslist formats nothing, so even an unsupported spec gives"money": []and"error": "".
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.