Python Module 2 – Values, variables, numbers and strings
f-strings and the format mini-language
Format numbers, text and dates with Python f-strings and format specifications (width, alignment, precision, grouping), debug with =, and read % and format().
What you will learn
- Format numbers, dates and text with f-strings and format specs
- Use the = specifier and !r conversion for debugging
- Read older % and str.format code
Before you start
On this page
Programs constantly turn values into text: a total on a receipt, a percentage in a report, a date on a ticket, a variable’s value while you hunt a bug. An f-string does that in one readable line, and the format specification after a colon inside it controls the details: how wide, how many decimals, which separator, which alignment.
Values in text with f-strings
Put f (or F) before the opening quote, and write expressions inside curly braces. Python evaluates each one when
the line runs and puts its value into the text:
item = "masala dosa"
price = 85
quantity = 3
# Expressions inside the braces run when the line runs, and their values are put in the text.
print(f"{quantity} × {item} cost {price * quantity}")
print(f"{item.title()} has {len(item)} characters")
# Without the f, braces are ordinary characters.
print("{quantity} × {item}")
# Doubled braces print one brace.
print(f"{{quantity}} is replaced by {quantity}") Output
3 × masala dosa cost 255
Masala Dosa has 11 characters
{quantity} × {item}
{quantity} is replaced by 3
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 first_fstrings.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
- Anything that is an expression can go in the braces: a name, arithmetic, a method call such as
item.title(). Unless a format specification (the next section) says otherwise, each value appears asstr()would write it. - Forget the
fand the braces are printed as typed, with no error to warn you. When you see{name}in your output, look for the missingffirst. - To print a brace itself, double it:
{{and}}.
Version note
Since Python 3.12 (PEP 701), an expression inside an f-string may use the same kind of quotes as the f-string
around it, as in f"{prices["tea"]}", may contain backslashes, and may run over several lines with comments.
Python 3.11 and older reject the same line with a SyntaxError, so code that must run on them uses the other kind
of quotes inside: f"{prices['tea']}".
Format specifications
After the expression, a colon starts a format specification, a short code that says how the value should look. This receipt uses widths, alignment, decimals and commas:
order = [("Masala dosa", 2, 85), ("Filter coffee", 3, 30), ("Idli", 4, 12.5), ("Party thali", 40, 260)]
print(f"{'Item':<15}{'Qty':>5}{'Price':>9}{'Amount':>12}")
total = 0
# Each item is a tuple (name, quantity, price); the loop takes them one at a time.
for name, quantity, price in order:
amount = quantity * price
total += amount
print(f"{name:<15}{quantity:>5}{price:>9.2f}{amount:>12,.2f}")
print(f"{'Total':<29}{total:>12,.2f}")
print(f"{' Thank you ':*^41}") Output
Item Qty Price Amount Masala dosa 2 85.00 170.00 Filter coffee 3 30.00 90.00 Idli 4 12.50 50.00 Party thali 40 260.00 10,400.00 Total 10,710.00 *************** Thank you ***************
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 receipt.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The header line formats strings: {'Item':<15} pads “Item” with spaces to 15 characters, aligned left. The item
lines format numbers: {price:>9.2f} writes the price with two decimals, right-aligned in 9 characters, and
{amount:>12,.2f} adds a comma between every three digits. The last line centres a text with ^ and fills the
spare room with *.
Every part of a specification is optional, but the parts that are present always come in this order:
- Fill and align, such as
*>:<left,>right,^centre, and=puts the padding after the sign. Any fill character may come before the align character. - Sign, such as
+:+shows a sign on every number, and a space puts a space before positive numbers. z(Python 3.11 and newer): turns a negative zero after rounding, such as-0.00, into0.00.#: the alternate form, which adds prefixes such as0xfor hexadecimal.0: pads numbers with zeros after the sign.- Width, such as
12: the minimum width in characters. Longer values are not cut. - Grouping, such as
,: a comma (or_) between groups of three digits. With the typesb,o,xandX,_groups in fours instead. - Precision, such as
.2: the digits after the point forf,eand%, the significant digits forg, and the most characters kept for a string. - Type, such as
f: how to write the value, for exampled,f,e,g,%,xorb.
Without an align character, strings line up on the left and numbers on the right. A width is only a minimum: a value longer than the width is printed in full, which keeps numbers correct but can push a column out of line.
Numbers
format(value, spec) applies one specification and returns the text, exactly as an f-string does after its colon.
This program runs a list of specifications through it:
# format(value, spec) applies one format specification; an f-string does the same after the colon.
examples = [
(1234567.891, ".2f"),
(1234567.891, ",.2f"),
(1234567.891, "_.0f"),
(1234567.891, ".3e"),
(1234567.891, ".3g"),
(0.256, ".1%"),
(42, "+d"),
(42, "06d"),
(255, "x"),
(255, "#X"),
(5, "08b"),
(-0.004, ".2f"),
(-0.004, "z.2f"),
]
for value, spec in examples:
print(f"{value!r:>11} {spec!r:7} → {format(value, spec)}") Output
1234567.891 '.2f' → 1234567.89
1234567.891 ',.2f' → 1,234,567.89
1234567.891 '_.0f' → 1_234_568
1234567.891 '.3e' → 1.235e+06
1234567.891 '.3g' → 1.23e+06
0.256 '.1%' → 25.6%
42 '+d' → +42
42 '06d' → 000042
255 'x' → ff
255 '#X' → 0XFF
5 '08b' → 00000101
-0.004 '.2f' → -0.00
-0.004 'z.2f' → 0.00
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 number_specs.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
fgives a fixed number of decimals andescientific notation.gpicks one of the two and drops zeros at the end; with.3git keeps three significant digits.%multiplies by 100 and adds a percent sign, so store fractions (0.256) rather than percentages.dis a whole number,xandXhexadecimal,bbinary andooctal;#adds the prefix (0X), and08bpads with zeros to eight places.- Rounding to a precision follows the same rules as
round(), on the binary value a float really holds, so a few decimal numbers round the “wrong” way; the floating-point lesson explains why. For money, keep whole paise or cents in an integer, or usedecimal.Decimal. -0.004rounds to-0.00, which looks odd on a report; thezoption writes0.00instead.
Version note
New in Python 3.14: the grouping option can also follow the precision, to group the digits after the decimal
point. Python 3.13 and older reject these specifications with a ValueError.
value = 1234567.891234
print(f"{value:,f}")
print(f"{value:,._f}")
print(f"{value:_._f}")
print(f"{0.000123456789:.10_f}") Output
1,234,567.891234 1,234,567.891_234 1_234_567.891_234 0.000_123_456_8
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 fraction_grouping.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
Lakh and crore
The , option always groups in threes, the international way: 1,234,567. Indian writing groups the last three
digits and then pairs, so one lakh is 1,00,000 and the same number is 12,34,567. No option of a format
specification does that on its own. The n type groups digits the way the current locale does, so an Indian locale
can give that grouping, but a program must first choose one with locale.setlocale(), and the locales differ from
one system to another (in the browser runner, n adds no separators at all). Programs that need Indian grouping
therefore usually do it themselves, as this lesson’s exercise does.
A common mistake: the wrong type code
Each type code belongs to some kinds of value. d is for integers, and a float does not convert itself silently:
price = 49.75
# A float with no decimals shown: the f type with precision 0 rounds it.
print(f"{price:.0f}")
# The d type is for integers only, so this line stops the program.
print(f"{price:d}") Output (exit status 1)
50
Printed as an error (standard error)
Traceback (most recent call last):
File "mistakes.py", line 7, in <module>
print(f"{price:d}")
^^^^^^^^^
ValueError: Unknown format code 'd' for object of type 'float'
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 mistakes.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
{price:.0f} is the way to show a float without decimals (it rounds). For text, only a fill with the alignments <,
> and ^, a width, a precision (which cuts the text short) and the type s make sense: {name:,} fails because
commas mean nothing for a string.
Dates and times
Dates and times have their own specification language: after the colon come the strftime codes of the datetime
module, such as %d for the day, %B for the month’s name and %H:%M for a 24-hour time:
from datetime import datetime
departure = datetime(2030, 3, 14, 6, 5)
# Dates and times take strftime codes after the colon.
print(f"{departure:%d %B %Y, %H:%M}")
print(f"{departure:%a %d %b}")
print(f"{departure:%Y-%m-%d %H:%M}")
print(f"Boarding at {departure:%I:%M %p}") Output
14 March 2030, 06:05 Thu 14 Mar 2030-03-14 06:05 Boarding at 06:05 AM
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 dates.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
%Y-%m-%d gives the international ISO 8601 order, which also sorts correctly as text. The names of days and months
come from the current locale. Whatever the computer’s language, a Python program starts with the plain C locale,
which uses English names, until it calls locale.setlocale().
Debugging with = and !r
Add = after the expression and the f-string prints the expression’s text, an equals sign and the value. It is the
quickest way to look at a few values while you hunt a bug:
total = 1234.5
city = "Pune "
print(f"{total=}")
print(f"{total = :,.2f}")
print(f"{city=}")
print(f"{city!r} has {len(city)} characters") Output
total=1234.5 total = 1,234.50 city='Pune ' 'Pune ' has 5 characters
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 debug_values.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
- The spaces you write around
=are kept, so{total = }printstotal = …. - A format specification can follow:
{total = :,.2f}. - Without a specification,
=shows the value withrepr(), so a string appears in quotes and its trailing space becomes visible. The conversion!rasks forrepr()anywhere (and!sforstr(),!aforascii()). Use it whenever spaces, empty strings or the difference between"7"and7might matter.
Reading older formatting code
f-strings arrived in Python 3.6, so older code, many libraries and a lot of answers online use the two styles that came before. They print the same result here:
name = "Asha"
score = 0.8765
# printf-style formatting with %, the oldest style.
print("%s scored %.1f%%" % (name, score * 100))
# str.format(), with the same mini-language as f-strings after each colon.
print("{} scored {:.1%}".format(name, score))
print("{0} scored {1:.1%}. Well done, {0}!".format(name, score))
print("{who} scored {mark:.1%}".format(who=name, mark=score))
# The f-string that does the same.
print(f"{name} scored {score:.1%}") Output
Asha scored 87.6% Asha scored 87.6% Asha scored 87.6%. Well done, Asha! Asha scored 87.6% Asha scored 87.6%
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 old_styles.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
- printf-style formatting with
%puts the values after the string:%sfor any value as text,%dfor an integer,%.1ffor a float with one decimal, and%%for a percent sign. You will meet it in theloggingmodule and in old code. The Python documentation lists its quirks: a single tuple or dictionary as the value is easy to get wrong. str.format()uses braces like an f-string, with the values given as arguments:{}takes the next one,{0}a position (which may repeat) and{who}a keyword. After a colon it uses the same mini-language as f-strings. It is still useful when the template is data, such as a message read from a translation file, because an f-string must be written in the code. Use only templates you control: a field such as{0.__class__}reads an attribute of the value it is given.
Security
Never paste user input into SQL, HTML or shell commands with an f-string or format(): input such as
x'; DROP TABLE users; -- would become part of the command. Databases take parameters (? placeholders in
sqlite3), html.escape() makes text safe to show in HTML (between tags or in a quoted attribute),
subprocess.run() given a list of arguments starts a program without a shell, so no value is read as a command,
and Python 3.14’s template strings (t-strings, PEP 750) let a library see each value before it is inserted. Later
lessons cover each of them.
Key takeaways
f"…{expression}…"puts the value of each expression into the text when the line runs; without thef, braces are printed as typed.- After a colon, a format specification controls the text: fill and align, sign,
z,#,0, width, grouping, precision and type, always in that order. ,.2fgives two decimals with commas,%a percentage,xandbhexadecimal and binary, and<,>and^align within a width; Python 3.14 can also group the digits after the point.,always groups in threes; other conventions, such as the Indian grouping in pairs, need your own code.{value=}and!rshow what a value really is while you debug.%formatting andstr.format()still appear in older code;format()uses the same mini-language as f-strings.
Exercise
Exercise · Medium · Python
Format rupees with Indian digit grouping
In India, large amounts group their digits in a different way from , in a format specification: the last three digits form a group, and every group before them has two digits. One lakh is 1,00,000 and one crore is 1,00,00,000.
Write format_inr(paise), which takes an amount of money as a whole number of paise (100 paise make one rupee) and returns it as rupees with that grouping and exactly two decimals:
format_inr(123456789)returns"₹12,34,567.89"andformat_inr(100000)returns"₹1,000.00".- Small amounts keep a zero before the point:
format_inr(99)returns"₹0.99"andformat_inr(0)returns"₹0.00". - A negative amount has its minus sign before the rupee sign:
format_inr(-150)returns"-₹1.50".
Work with whole numbers (divmod() splits the paise into rupees and the paise left over), not with floats, so that no paisa is lost to rounding. The sample tests import your function from rupees.py.
Starter code · rupees.py
def format_inr(paise):
"""Return an amount in paise as rupees with Indian digit grouping, such as '₹12,34,567.89'."""
# Replace this line with your code.
return "" The sample tests · test_rupees.py
from rupees import format_inr
def test_small_amounts():
"""keeps a zero rupee before the point and two digits of paise"""
assert format_inr(0) == "₹0.00"
assert format_inr(5) == "₹0.05"
assert format_inr(99) == "₹0.99"
assert format_inr(100) == "₹1.00"
def test_thousands():
"""puts the first comma before the last three digits"""
assert format_inr(99999) == "₹999.99"
assert format_inr(100000) == "₹1,000.00"
assert format_inr(9999999) == "₹99,999.99"
def test_lakh_and_crore():
"""groups the digits before the last three in pairs"""
assert format_inr(10000000) == "₹1,00,000.00"
assert format_inr(123456789) == "₹12,34,567.89"
assert format_inr(1000000000) == "₹1,00,00,000.00"
assert format_inr(98765432100) == "₹98,76,54,321.00"
def test_negative():
"""puts the minus sign before the rupee sign"""
assert format_inr(-150) == "-₹1.50"
assert format_inr(-123456789) == "-₹12,34,567.89"
def test_very_large():
"""keeps grouping in pairs beyond a crore, with no rounding"""
assert format_inr(10**14 + 1) == "₹10,00,00,00,00,000.01"
assert format_inr(123456789012345678) == "₹1,23,45,67,89,01,23,456.78" A hint
rupees, rest = divmod(abs(paise), 100) gives the two parts, and f"{rest:02d}" writes the paise with two digits. For the grouping, turn the rupees into text with str(), split off the last three digits with digits[-3:], and then, while the part in front (digits[:-3]) is longer than two digits, keep taking two digits off its end with head[-2:] and head[:-2]. Collect the groups in a list and finish with ",".join(groups).
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Check yourself
5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.
References
- The Python Tutorial: Formatted String Literals (Python Software Foundation)
- Format Specification Mini-Language (string, Common string operations) (Python Software Foundation)
- Formatted String Literals, f-strings (The Python Standard Library) (Python Software Foundation)
- f-strings (The Python Language Reference) (Python Software Foundation)
- PEP 701: Syntactic formalization of f-strings (Python Software Foundation)
- printf-style String Formatting (The Python Standard Library) (Python Software Foundation)
- strftime() and strptime() format codes (datetime) (Python Software Foundation)
- What's New In Python 3.14: built-ins (Python Software Foundation)
- locale, Internationalization services (Python Software Foundation)
- How to use placeholders to bind values in SQL queries (sqlite3) (Python Software Foundation)
- html.escape() (html, HyperText Markup Language support) (Python Software Foundation)
- PEP 750: Template Strings (Python Software Foundation)
- Format String Syntax (string, Common string operations) (Python Software Foundation)
- Security Considerations (subprocess, Subprocess management) (Python Software Foundation)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress