Coding Patterns and Idioms#
This notebook is a recipe reference — practical Python idioms used throughout the book. Return here when you encounter a pattern you want to understand or reuse.
Topics
List comprehensions
Lambda functions
Docstrings and
help()Type hints
enumerate()andzip()try/exceptfor error handlingf-string formatting
List Comprehension and Lambda#
These are concise tools for transformation logic in scripts.
Comprehension: readable filtering/transformation in one expression
Lambda: lightweight anonymous function defined inline, without
def
List Comprehension#
A list comprehension produces a new list by applying an expression to each item in an iterable, with an optional filter condition:
[expression for item in iterable if condition]
names = ['alice', 'bob', 'charlie', 'amy']
# filter: keep names starting with 'a'
starts_with_a = [n for n in names if n.startswith('a')]
# transform: capitalize each name
capitalized = [n.capitalize() for n in names]
# filter + transform combined
short_upper = [n.upper() for n in names if len(n) <= 3]
print(starts_with_a)
print(capitalized)
print(short_upper)
['alice', 'amy']
['Alice', 'Bob', 'Charlie', 'Amy']
['BOB', 'AMY']
Lambda#
A lambda is an anonymous function defined in a single expression. The syntax is:
lambda parameters: expression
It takes any number of parameters but only one expression — no statements, no
return.The expression is implicitly returned.
Lambdas are best used inline, as a
key=argument or passed tomap()/filter(). For anything more complex, usedefinstead.
Use case |
Example |
|---|---|
Single argument |
|
Multiple arguments |
|
As a |
|
With |
|
With |
|
# basic lambda — equivalent to def double(x): return x * 2
double = lambda x: x * 2
print(double(5)) # 10
# multiple arguments
add = lambda x, y: x + y
print(add(3, 4)) # 7
# inline — no assignment needed
print((lambda x: x ** 2)(9)) # 81
10
7
81
names = ['alice', 'bob', 'charlie', 'amy']
nums = [3, -1, 4, -1, 5, -9, 2, 6]
# sorted(): sort by length
by_length = sorted(names, key=lambda s: len(s))
# map(): square every number
squares = list(map(lambda x: x ** 2, nums))
# filter(): keep only positive numbers
positives = list(filter(lambda x: x > 0, nums))
print(by_length)
print(squares)
print(positives)
['bob', 'amy', 'alice', 'charlie']
[9, 1, 16, 1, 25, 81, 4, 36]
[3, 4, 5, 2, 6]
### Exercise: Comprehension + Lambda
# Given: numbers = [1, 2, 3, 4, 5, 6]
# 1) Use a list comprehension to produce the squares of even numbers.
# 2) Write a lambda that triples a value, then apply it to all numbers with map().
# 3) Use filter() with a lambda to keep only numbers greater than 3.
### Your code starts here.
### Your code ends here.
[4, 16, 36]
[3, 6, 9, 12, 15, 18]
[4, 5, 6]
Docstrings and help()#
A docstring is a string literal placed immediately after a def (or class) statement. Python attaches it to the object and help() displays it. It is the standard way to document what a function does, what it expects, and what it returns.
def function_name(param):
"""One-line summary of what the function does.
Longer explanation if needed. Describe parameters and return value.
Args:
param: description of the parameter.
Returns:
description of the return value.
"""
Write a docstring for every function you intend to reuse or share. It costs almost nothing and saves significant time later.
def add_tax(price, rate=0.07):
"""Return price with sales tax applied.
Args:
price: the pre-tax amount (float or int).
rate: tax rate as a decimal (default 0.07 = 7%).
Returns:
float: the post-tax amount.
"""
return price * (1 + rate)
# help() reads the docstring
help(add_tax)
# __doc__ gives you the raw string
print(add_tax.__doc__)
Help on function add_tax in module __main__:
add_tax(price, rate=0.07)
Return price with sales tax applied.
Args:
price: the pre-tax amount (float or int).
rate: tax rate as a decimal (default 0.07 = 7%).
Returns:
float: the post-tax amount.
Return price with sales tax applied.
Args:
price: the pre-tax amount (float or int).
rate: tax rate as a decimal (default 0.07 = 7%).
Returns:
float: the post-tax amount.
Type Hints#
Type hints annotate function parameters, return values, and sometimes variables with their expected types. Python does not enforce them at runtime — they are documentation for readers and tools like linters, IDEs, and static type checkers.
def greet(name: str) -> str:
return f"Hello, {name}"
Annotation |
Meaning |
|---|---|
|
parameter |
|
the function returns a string |
|
the function returns nothing useful |
|
a list of integers |
|
a dictionary with string keys and float values |
`x: int |
None` |
Use type hints for any module-level function you share with others. They act as a lightweight contract that makes code significantly easier to read and debug.
Collection Type Hints#
For collections, put the item type inside square brackets. This says not only that the value is a list, dictionary, tuple, or set, but also what kind of values it contains.
scores: list[int] = [88, 92, 79]
prices: dict[str, float] = {"coffee": 2.50, "tea": 2.00}
point: tuple[float, float] = (3.5, 4.0)
unique_ids: set[int] = {101, 102, 103}
Optional Values and Unions#
Some functions may return either a normal value or None. In modern Python, write that with the union operator |:
def find_score(name: str, scores: dict[str, int]) -> int | None:
return scores.get(name)
Read int | None as “an integer or None.” More generally, A | B means “either type A or type B”:
def parse_id(text: str) -> int | str:
if text.isdigit():
return int(text)
return text
Older Python code often uses names from the typing module:
from typing import Optional, Union
def find_score_old(name: str, scores: dict[str, int]) -> Optional[int]:
return scores.get(name)
def parse_id_old(text: str) -> Union[int, str]:
if text.isdigit():
return int(text)
return text
Optional[int] means the same thing as int | None; Union[int, str] means the same thing as int | str.
def add_tax(price: float, rate: float = 0.07) -> float:
"""Return price with sales tax applied."""
return price * (1 + rate)
def first_word(sentence: str) -> str:
"""Return the first word of a sentence."""
return sentence.split()[0]
def repeat(items: list, n: int) -> list:
"""Return a new list with items repeated n times."""
return items * n
print(add_tax(49.99))
print(first_word("hello world"))
print(repeat([1, 2], 3))
53.48930000000001
hello
[1, 2, 1, 2, 1, 2]
enumerate() and zip()#
These two built-ins appear constantly in loops throughout the book.
enumerate(iterable, start=0)#
Returns (index, value) pairs. Use it whenever you need the position of an item while iterating — no manual counter variable needed.
zip(iter1, iter2, ...)#
Pairs up items from multiple iterables, stopping at the shortest. Use it to iterate two related sequences in lockstep — no index needed.
fruits = ['apple', 'banana', 'cherry']
prices = [1.20, 0.50, 2.00]
# enumerate — index + value
for i, fruit in enumerate(fruits):
print(f"{i}: {fruit}")
print()
# enumerate with a custom start index
for i, fruit in enumerate(fruits, start=1):
print(f"{i}. {fruit}")
print()
# zip — pair two lists
for fruit, price in zip(fruits, prices):
print(f"{fruit}: ${price:.2f}")
print()
# zip into a dict
price_map = dict(zip(fruits, prices))
print(price_map)
0: apple
1: banana
2: cherry
1. apple
2. banana
3. cherry
apple: $1.20
banana: $0.50
cherry: $2.00
{'apple': 1.2, 'banana': 0.5, 'cherry': 2.0}
try / except — Error Handling#
When your code does something that might fail — opening a file, converting user input, accessing a key — wrap it in a try block so the program can recover gracefully instead of crashing.
try:
# code that might raise an exception
except SomeError:
# what to do when it fails
else:
# runs only if no exception was raised (optional)
finally:
# always runs, even if an exception occurred (optional)
Catch specific exceptions — avoid bare except: because it silently swallows every error including bugs.
Common exception |
Triggered by |
|---|---|
|
Wrong type of value ( |
|
Wrong type ( |
|
Missing dict key |
|
List index out of range |
|
File doesn’t exist |
|
Divide by zero |
def safe_divide(a: float, b: float) -> float | None:
"""Divide a by b; return None if b is zero."""
try:
return a / b
except ZeroDivisionError:
print("Error: cannot divide by zero")
return None
print(safe_divide(10, 2)) # 5.0
print(safe_divide(10, 0)) # Error message, then None
# ValueError: converting invalid input
def parse_int(text: str) -> int | None:
"""Convert text to int; return None on failure."""
try:
return int(text)
except ValueError:
print(f"Could not convert {text!r} to int")
return None
print(parse_int("42")) # 42
print(parse_int("hello")) # error message, then None
# FileNotFoundError: reading a missing file
from pathlib import Path
def read_file(path: str) -> str | None:
"""Read a file and return its contents, or None if file not found."""
try:
return Path(path).read_text(encoding='utf-8')
except FileNotFoundError:
print(f"File not found: {path}")
return None
print(read_file("nonexistent.txt"))
5.0
Error: cannot divide by zero
None
42
Could not convert 'hello' to int
None
File not found: nonexistent.txt
None
f-String Formatting#
You have used f-strings for basic interpolation (f"Hello, {name}"). They support a rich format spec inside the braces that controls alignment, padding, decimal places, and thousands separators — useful for tables and reports throughout the book.
f"{value:{width}.{precision}{type}}"
Spec |
Meaning |
Example |
Output |
|---|---|---|---|
|
right-pad to width 10 (strings left-align by default) |
|
|
|
right-align in 10 chars |
|
|
|
left-align |
|
|
|
center |
|
|
|
float, 2 decimal places |
|
|
|
float with thousands comma |
|
|
|
zero-padded, width 8 |
|
|
|
scientific notation |
|
|
|
percentage |
|
|
items = [
('apple', 1.2, 842),
('banana', 0.5, 3201),
('cherry', 2.0, 150),
]
# aligned table using format specs
print(f"{'Item':<10} {'Price':>8} {'Sold':>8} {'Revenue':>12}")
print('-' * 42)
for name, price, sold in items:
revenue = price * sold
print(f"{name:<10} {price:>8.2f} {sold:>8,} {revenue:>12,.2f}")
print()
# percentage and scientific notation
score = 0.857
tiny = 0.000123
print(f"Accuracy : {score:.1%}")
print(f"Epsilon : {tiny:.2e}")
Item Price Sold Revenue
------------------------------------------
apple 1.20 842 1,010.40
banana 0.50 3,201 1,600.50
cherry 2.00 150 300.00
Accuracy : 85.7%
Epsilon : 1.23e-04