Python F-String & Format Spec Cheat Sheet
1 · The lesson
readAn f-string is a string with f before the quotes. Python runs the code inside each pair of {} and puts the result into the text. After a colon you can add a format spec: a short code that says how to show the value, such as 2 decimal places or a width of 10 characters. For the full tutorial, see F-strings.
1. Put values in
Any Python expression works inside the braces: a variable, a sum, a method call, an index.
name, n, price = "Ada", 42, 1234.5 f"Hi {name}" # 'Hi Ada' f"{n + 1} items" # '43 items' f"{name.upper()}" # 'ADA' f"{name[0]}." # 'A.' f"{'odd' if n % 2 else 'even'}" # 'even' (an if/else inside the braces) f"{[1, 2]}" # '[1, 2]' (a list looks the same as when you print it)
2. The parts of a format spec
The parts go in this fixed order. Every part is optional.
{value:[fill][align][sign][#][0][width][,][.precision][type]}| Part | Example | What it does |
|---|---|---|
fill | * | The character used for padding (a space by default) |
align | < > ^ = | Left, right, centre, or padding between the sign and the digits |
sign | + or a space | Show a + before positive numbers, or a space |
# | # | Add a prefix: 0x for hex, 0b for binary, 0o for octal |
0 | 0 | Pad a number with zeros |
width | 10 | The smallest number of characters to use |
, or _ | , | Put a separator between thousands |
.precision | .2 | Decimal places for a number. The most characters for text |
type | f d e x % | How to show the number: decimals, whole number, scientific, hex, percent |
3. Width and alignment
Text lines up on the left by default, and numbers line up on the right. A precision on text cuts it short.
f"[{name:<6}]" # '[Ada ]' (left) f"[{name:>6}]" # '[ Ada]' (right) f"[{name:^7}]" # '[ Ada ]' (centre) f"[{name:*^7}]" # '[**Ada**]' (the fill character goes before < > ^) f"[{n:6}]" # '[ 42]' f"[{-n:=6}]" # '[- 42]' (= puts the padding between the sign and the digits) f"{name:.2}" # 'Ad' f"{'Lovelace':.3}…" # 'Lov…' (cut a long name to fit a column)
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) name = _AutoMock('name') n = 1 # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'name, n, price = "Ada", 42, 1234.5\nf"Hi {name}" # \'Hi Ada\'\nf"{n + 1} items" # \'43 items\'\nf"{name.upper()}" # \'ADA\'\nf"{name[0]}." # \'A.\'\nf"{\'odd\' if n % 2 else \'even\'}" # \'even\' (an if/else inside the braces)\nf"{[1, 2]}" # \'[1, 2]\' (a list looks the same as when you print it)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
4. Decimals and big numbers
f"{3.14159:.2f}" # '3.14' (f: a fixed number of decimal places) f"{2.5:.0f}" # '2' (a half rounds to the even number, like round()) f"{price:,.2f}" # '1,234.50' f"{1000000:_}" # '1_000_000' f"{0.256:.1%}" # '25.6%' (% multiplies by 100 for you) f"{6.022e23:.3e}" # '6.022e+23' (scientific notation) f"{0.000123:.2g}" # '0.00012' (g: 2 significant figures) f"{price:10.2f}" # ' 1234.50' (the width of 10 includes the point and the decimals)
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) price = _AutoMock('price') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'name, n, price = "Ada", 42, 1234.5\nf"Hi {name}" # \'Hi Ada\'\nf"{n + 1} items" # \'43 items\'\nf"{name.upper()}" # \'ADA\'\nf"{name[0]}." # \'A.\'\nf"{\'odd\' if n % 2 else \'even\'}" # \'even\' (an if/else inside the braces)\nf"{[1, 2]}" # \'[1, 2]\' (a list looks the same as when you print it)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
5. Signs and zero padding
A 0 just before the width pads a number with zeros. The zeros go after the sign.
f"{n:+}" # '+42' (always show the sign) f"{n: }" # ' 42' (a space where the + would go) f"{7:03}" # '007' f"{-7:04}" # '-007' f"{3.14:07.2f}" # '0003.14' (the width counts the point too) f"INV-{n:06}" # 'INV-000042' (invoice numbers with a fixed length)
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) n = _AutoMock('n') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'name, n, price = "Ada", 42, 1234.5\nf"Hi {name}" # \'Hi Ada\'\nf"{n + 1} items" # \'43 items\'\nf"{name.upper()}" # \'ADA\'\nf"{name[0]}." # \'A.\'\nf"{\'odd\' if n % 2 else \'even\'}" # \'even\' (an if/else inside the braces)\nf"{[1, 2]}" # \'[1, 2]\' (a list looks the same as when you print it)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
6. Binary, octal and hexadecimal
f"{255:x}" # 'ff' (X gives upper case: FF) f"{255:#x}" # '0xff' (# adds the 0x prefix) f"{255:#010x}" # '0x000000ff' (# with 0 and a width of 10) f"{10:b}" # '1010' (binary) f"{10:o}" # '12' (octal) f"{5:08b}" # '00000101' (a full byte: 8 binary digits) f"#{255:02x}{0:02x}{128:02x}" # '#ff0080' (red, green and blue as a hex colour)
7. Dates and times
For a date or a time, the format spec uses the same codes as strftime(): %Y is the year, %m the month, %d the day. See the datetime cheat sheet for the full list.
from datetime import datetime d = datetime(2026, 3, 14, 9, 30) f"{d:%Y-%m-%d}" # '2026-03-14' f"{d:%d %b %Y}" # '14 Mar 2026' f"{d:%A, %H:%M}" # 'Saturday, 09:30' f"{d:%I:%M %p}" # '09:30 AM' (a 12-hour clock) fmt = "%d/%m/%y" f"{d:{fmt}}" # '14/03/26' (the format kept in a variable) f"{d.day}/{d.month}" # '14/3' (no zero padding, unlike %d)
8. Debugging
Put = after the expression and the f-string shows the expression too. The = form is a quick way to see values while you look for a bug.
f"{n=}" # 'n=42' f"{n * 2 = }" # 'n * 2 = 84' (the spaces around = are kept) f"{price=:,.1f}" # 'price=1,234.5' (a format spec still works after the =) f"{name!r}" # "'Ada'" (!r uses repr(), which shows the quotes) f"{name=}" # "name='Ada'" f"{'café'!a}" # "'caf\\xe9'" (!a uses ascii(), which escapes letters like é) f"{name!r:>8}" # " 'Ada'" (the ! part comes first, then the colon)
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) n = 1 price = _AutoMock('price') name = _AutoMock('name') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'name, n, price = "Ada", 42, 1234.5\nf"Hi {name}" # \'Hi Ada\'\nf"{n + 1} items" # \'43 items\'\nf"{name.upper()}" # \'ADA\'\nf"{name[0]}." # \'A.\'\nf"{\'odd\' if n % 2 else \'even\'}" # \'even\' (an if/else inside the braces)\nf"{[1, 2]}" # \'[1, 2]\' (a list looks the same as when you print it)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
9. Nested fields
A format spec can contain its own {}, so the width, the precision or the whole spec can come from a variable.
w, p, fill = 10, 3, "*" f"{3.14159:{w}.{p}f}" # ' 3.142' f"{'Ada':{fill}^{w}}" # '***Ada****' spec = ",.2f" f"{1234.5:{spec}}" # '1,234.50'
10. Braces, quotes and long lines
To show a real { or }, write it twice. Since Python 3.12 you can use the same quotes and a backslash inside the braces. In older versions, use the other kind of quote.
f"{{n}} is {n}" # '{n} is 42' f"{name.replace('a', 'o')}" # 'Ado' msg = (f"Name: {name}\n" f"Age: {n}") # two strings side by side join into one report = f"""Name: {name} Score: {97:>5}/100""" # triple quotes keep the line breaks print(report)
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) n = _AutoMock('n') name = _AutoMock('name') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'name, n, price = "Ada", 42, 1234.5\nf"Hi {name}" # \'Hi Ada\'\nf"{n + 1} items" # \'43 items\'\nf"{name.upper()}" # \'ADA\'\nf"{name[0]}." # \'A.\'\nf"{\'odd\' if n % 2 else \'even\'}" # \'even\' (an if/else inside the braces)\nf"{[1, 2]}" # \'[1, 2]\' (a list looks the same as when you print it)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
Name: Ada Score: 97/100
11. Tables and reports
Use the same widths for the header and for each row. Then the columns line up.
rows = [("tea", 3.5), ("cake", 12.0)] print(f"{'Item':<8}{'Cost':>8}") for item, cost in rows: print(f"{item:<8}{cost:>8,.2f}") print(f"{'':-^16}") print(f"{'Total':.<9}{15.5:>7,.2f}")
Item Cost tea 3.50 cake 12.00 ---------------- Total.... 15.50
12. When not to use an f-string
An f-string runs where you write it, so it cannot be a template kept for later. For a template stored in a variable or a file, use str.format(). For logging, pass the values as arguments: the logger fills them in only if it writes the line.
tpl = "{name} has {n} messages" tpl.format(name="Hedy", n=3) # 'Hedy has 3 messages' format(1234.5, ",.2f") # '1,234.50' (the same spec, without a string around it)
Common mistakes
Forgetting the f. "Hi {name}" prints the braces exactly as typed. Write f"Hi {name}".
A number spec on text. f"{name:.2f}" raises ValueError: Unknown format code 'f' for object of type 'str'. If the text holds a number, convert it first: f"{float('3.5'):.2f}".
Expecting exact rounding. A float is stored in binary, so some values are a tiny bit smaller than they look:
f"{2.675:.2f}" # '2.67'
Use the decimal module for money.
Expecting the width to cut text. The width is a minimum, so long text is never cut. Add a precision to cut it:
f"{'Lovelace':5}" # 'Lovelace' f"{'Lovelace':.5}" # 'Lovel'
A comma outside the spec. f"{n,}" makes a tuple, because the comma comes before the colon:
f"{n,}" # '(42,)' f"{n:,}" # '42'
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) n = _AutoMock('n') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'name, n, price = "Ada", 42, 1234.5\nf"Hi {name}" # \'Hi Ada\'\nf"{n + 1} items" # \'43 items\'\nf"{name.upper()}" # \'ADA\'\nf"{name[0]}." # \'A.\'\nf"{\'odd\' if n % 2 else \'even\'}" # \'even\' (an if/else inside the braces)\nf"{[1, 2]}" # \'[1, 2]\' (a list looks the same as when you print it)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
Parts in the wrong order. f"{n:5+}" raises ValueError. The sign comes before the width: f"{n:+5}".
Doing maths with the result. An f-string gives text, so f"{price:.2f}" + 1 raises TypeError. Keep the number for calculations and format it only when you show it.
.2 without a type. Without f, the precision means significant figures, not decimal places:
f"{price:.2}" # '1.2e+03' f"{price:.2f}" # '1234.50'
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) price = _AutoMock('price') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'name, n, price = "Ada", 42, 1234.5\nf"Hi {name}" # \'Hi Ada\'\nf"{n + 1} items" # \'43 items\'\nf"{name.upper()}" # \'ADA\'\nf"{name[0]}." # \'A.\'\nf"{\'odd\' if n % 2 else \'even\'}" # \'even\' (an if/else inside the braces)\nf"{[1, 2]}" # \'[1, 2]\' (a list looks the same as when you print it)', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
See also
Practice this
on practicepython.inShort exercises that run in your browser and tell you what your code did, as well as whether a test passed.
Found this useful? Share it with a friend who’s learning Python.