aymengharbi

live in prod, no console errors (yet)

Python cheatsheet

The syntax and standard library I reach for every week

On this page
  1. Basics
  2. Truthiness
  3. Operators
  4. Strings
  5. f-strings
  6. Collections
  7. Lists
  8. Tuples
  9. Dictionaries
  10. Sets
  11. Comprehensions
  12. Functions
  13. Lambdas, decorators and closures
  14. Generators
  15. Classes
  16. Files and paths
  17. JSON and CSV
  18. Errors
  19. Standard library worth knowing
  20. Type hints
  21. Async basics
  22. Environments and tooling
  23. Gotchas

A short reference for everyday Python 3. It covers what I look up most often, with the smallest example that shows the idea.

Basics

x = 10                 # int
pi = 3.14              # float
name = "Aymen"         # str
ok = True              # bool
nothing = None         # absence of a value

a, b = 1, 2            # unpacking
a, b = b, a            # swap
first, *rest = [1, 2, 3, 4]   # first=1, rest=[2, 3, 4]

Useful built-ins: len, range, enumerate, zip, sorted, reversed, sum, min, max, any, all, abs, round.

for i, value in enumerate(["a", "b", "c"], start=1):
    print(i, value)

for left, right in zip([1, 2, 3], ["x", "y", "z"]):
    print(left, right)

print(any(n > 2 for n in [1, 2, 3]))   # True
print(all(n > 0 for n in [1, 2, 3]))   # True

Truthiness

Empty containers, 0, 0.0, "" and None are falsy. Everything else is truthy.

if not items:        # empty list, dict, set or string
    print("nothing here")

value = user_input or "default"   # first truthy operand

Operators

Operator Meaning Example
// floor division 7 // 2 gives 3
% modulo 7 % 2 gives 1
** power 2 ** 10 gives 1024
is identity x is None
in membership "a" in "cat"
:= assignment expression if (n := len(xs)) > 3:

Strings

s = "Hello, world"
s.lower(); s.upper(); s.title()
s.strip()                  # trim whitespace on both sides
s.split(", ")              # ['Hello', 'world']
", ".join(["a", "b"])      # 'a, b'
s.replace("world", "you")
s.startswith("Hello")
s.find("world")            # index, or -1
s[0]; s[-1]; s[0:5]; s[::-1]

f-strings

name, score = "Aymen", 0.8734
print(f"{name} scored {score:.1%}")     # Aymen scored 87.3%
print(f"{1234567:,}")                   # 1,234,567
print(f"{name!r:>12}")                  # repr, right-aligned in 12 chars
print(f"{score=}")                      # score=0.8734

Collections

Lists

xs = [3, 1, 2]
xs.append(4)
xs.extend([5, 6])
xs.insert(0, 9)
xs.pop()            # remove and return the last item
xs.remove(1)        # remove the first match
xs.sort()           # in place
sorted(xs, key=lambda n: -n)   # new list
xs[1:3]; xs[::-1]; xs.copy()

Tuples

Immutable and hashable, so they can be dictionary keys.

point = (3, 4)
x, y = point

Dictionaries

user = {"name": "Aymen", "role": "lead"}
user["team"] = "data"
user.get("age")               # None instead of KeyError
user.get("age", 0)            # with a default
user.setdefault("tags", [])
user.update({"role": "lead dev"})

for key, value in user.items():
    print(key, value)

merged = {**user, "city": "Paris"}   # copy with an override
combined = user | {"city": "Paris"}  # Python 3.9+

Sets

a, b = {1, 2, 3}, {3, 4}
a | b        # union
a & b        # intersection
a - b        # difference
a ^ b        # symmetric difference
set(xs)      # deduplicate, order is not kept

Comprehensions

squares = [n * n for n in range(10)]
evens = [n for n in range(10) if n % 2 == 0]
lookup = {word: len(word) for word in ["a", "bb", "ccc"]}
unique = {n % 3 for n in range(10)}
total = sum(n * n for n in range(10))     # generator, no list is built
flat = [item for row in matrix for item in row]

Functions

def greet(name: str, *, loud: bool = False) -> str:
    message = f"Hello, {name}"
    return message.upper() if loud else message

greet("Aymen", loud=True)
  • *args collects extra positional arguments into a tuple.
  • **kwargs collects extra keyword arguments into a dict.
  • Parameters after a bare * are keyword-only.
  • Never use a mutable default value such as def f(xs=[]). Use None and create the list inside.
def add_item(item, items=None):
    items = [] if items is None else items
    items.append(item)
    return items

Lambdas, decorators and closures

people.sort(key=lambda p: p["age"])

import functools, time

def timed(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        try:
            return func(*args, **kwargs)
        finally:
            print(f"{func.__name__} took {time.perf_counter() - start:.3f}s")
    return wrapper

@timed
def work():
    ...

Generators

def read_chunks(items, size):
    for i in range(0, len(items), size):
        yield items[i : i + size]

list(read_chunks([1, 2, 3, 4, 5], 2))   # [[1, 2], [3, 4], [5]]

Classes

from dataclasses import dataclass, field

@dataclass(frozen=True)
class Project:
    name: str
    year: int
    tags: list[str] = field(default_factory=list)

    @property
    def label(self) -> str:
        return f"{self.name} ({self.year})"

p = Project("Gtag", 2024)
print(p.label)

A plain class, for comparison:

class Counter:
    def __init__(self, start: int = 0):
        self.value = start

    def increment(self) -> int:
        self.value += 1
        return self.value

    def __repr__(self) -> str:
        return f"Counter({self.value})"

Files and paths

from pathlib import Path

path = Path("data") / "notes.txt"
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("hello\n", encoding="utf-8")
text = path.read_text(encoding="utf-8")

for file in Path("data").glob("*.csv"):
    print(file.name, file.stat().st_size)

with open(path, encoding="utf-8") as handle:
    for line in handle:
        print(line.rstrip())

JSON and CSV

import csv, json

data = json.loads('{"a": 1}')
Path("out.json").write_text(json.dumps(data, indent=2), encoding="utf-8")

with open("rows.csv", newline="", encoding="utf-8") as handle:
    for row in csv.DictReader(handle):
        print(row["name"])

Errors

try:
    value = int(raw)
except ValueError as error:
    print(f"not a number: {error}")
except (KeyError, IndexError):
    print("missing")
else:
    print("parsed", value)
finally:
    print("always runs")

raise ValueError("something is wrong")

Define your own exception when callers need to catch it specifically:

class ConfigError(Exception):
    pass

Standard library worth knowing

from collections import Counter, defaultdict, deque
from itertools import chain, groupby, islice, product
from datetime import datetime, timedelta, timezone
from functools import lru_cache, partial

Counter("banana").most_common(2)      # [('a', 3), ('n', 2)]

groups = defaultdict(list)
for word in ["apple", "avocado", "banana"]:
    groups[word[0]].append(word)

now = datetime.now(timezone.utc)
tomorrow = now + timedelta(days=1)
print(now.isoformat())

@lru_cache(maxsize=None)
def fib(n: int) -> int:
    return n if n < 2 else fib(n - 1) + fib(n - 2)

Type hints

from typing import Literal, Protocol

def mean(values: list[float]) -> float:
    return sum(values) / len(values)

def find(user_id: int) -> dict[str, str] | None:
    ...

Mode = Literal["read", "write"]

class Closeable(Protocol):
    def close(self) -> None: ...

Hints are not enforced at runtime. Run a checker such as mypy or pyright to use them.

Async basics

import asyncio

async def fetch(n: int) -> int:
    await asyncio.sleep(0.1)
    return n * 2

async def main():
    results = await asyncio.gather(fetch(1), fetch(2), fetch(3))
    print(results)   # [2, 4, 6]

asyncio.run(main())

Environments and tooling

python -m venv .venv
source .venv/bin/activate
python -m pip install requests
python -m pip freeze > requirements.txt
python -m pytest -q
python -m http.server 8000

Gotchas

  • == compares values, is compares identity. Use is only for None, True and False.
  • A default mutable argument is shared between calls.
  • Copying a list with b = a makes a second name for the same list. Use a.copy() or a[:], and copy.deepcopy for nested data.
  • Modifying a list while looping over it skips items. Loop over a copy or build a new list.
  • A late-binding closure in a loop sees the final value of the loop variable. Bind it with a default argument: lambda i=i: i.