DS DevShelfHub Projects · AI tools
Cheatsheets / Python
Cheatsheet · Languages

Python Cheatsheet: Comprehensions, Typing and Async Reference

By DevShelfHub

Comprehensions, dataclasses, typing, async, pathlib, itertools — the standard-library surface a Python dev actually uses.

105 items 7 min Stdlib Typing Async

Start hereQuick start · 6 you’ll reach for daily

Virtualenvpython -m venv .venv
Format / lintruff format . && ruff check .
Type checkmypy --strict src/
Testpytest -x --ff
Run as modulepython -m my_pkg.cli
Drop into REPLpython -i script.py

Target versions · paceVersions

Targets: python ≥ 3.11 typing: pep 604 / 695 pip / uv / pipx

This sheet pins to Python 3.11+. match / TaskGroup / tomllib / PEP 604 union syntax (int | None) are assumed. For 3.12+ also type X = ... aliases (PEP 695). Drop a version target lower only if you have to.

interpreter · venv · depsSetup

bash
# Manage Python itself
pyenv install 3.13.1            # if you use pyenv
uv python install 3.13          # if you use uv (recommended)

# Per-project virtualenv
python -m venv .venv && source .venv/bin/activate     # stdlib
uv venv && source .venv/bin/activate                  # uv (faster)

# Dependencies
pip install -r requirements.txt                       # classic
uv pip install -r requirements.txt                    # uv (drop-in, ~10x)
uv add httpx pydantic                                 # add + lock + install

# Run
python -m my_pkg.cli                                  # treat as module
python -c "import sys; print(sys.version_info)"

where things liveStandard library map

A field guide for “is there a stdlib for this?”. Reach for stdlib before adding a dependency.

pathlibFilesystem paths as objects. Default over os.path.
dataclassesPlain typed records. slots, frozen, kw_only.
enumSymbolic constants. Use StrEnum (3.11+) for serialisable enums.
typingLiteral, Protocol, TypedDict, NewType.
collectionsdefaultdict, Counter, deque, ChainMap.
itertoolsCombinators: chain, groupby, accumulate, pairwise.
functoolslru_cache, cache, partial, cached_property, reduce.
contextlibcontextmanager, suppress, ExitStack.
asyncioAsync runtime. TaskGroup, timeout, Queue.
concurrent.futuresThread / process pools with Executor.
subprocessRun external commands. Prefer run([..], check=True).
json / tomllib / csvBuilt-in parsers. tomllib is read-only TOML (3.11+).
argparse / shlexCLI parsing & safe command splitting.
datetime / zoneinfoDates + timezones. Use datetime.now(tz=…), never naive.
loggingStdlib logger. logging.basicConfig(level=…) in entry point only.
unittest.mockpatch, MagicMock. Works with pytest fine.

static typesTyping

int | NonePreferred 3.10+ union syntax.
Optional[int]Legacy Equivalent to int | None.
list[int], dict[str, int]Built-in generics (PEP 585). No need to import.
type Vec = list[float]Type alias (PEP 695, 3.12+).
Literal["low", "med", "high"]Constrain to specific values.
SelfReturn type for fluent / factory methods.
TypedDictStructured dicts. Cheaper than a class for boundary types.
ProtocolStructural (“duck”) typing. Anything matching the shape passes.
NewType("UserId", int)Wrap a primitive for stricter checking. Zero runtime cost.
cast(T, x)Tell the checker “trust me”. No runtime conversion.
if TYPE_CHECKING:Import only for the checker. Avoid runtime cycles.
@overloadMultiple signatures for the same function (e.g. def f(x: int) -> str; def f(x: str) -> int).
assert_never(x)Exhaustiveness check inside match.

typed recordsDataclasses

@dataclassAuto __init__, __repr__, __eq__.
@dataclass(frozen=True)Immutable. Hashable. Use for value objects.
@dataclass(slots=True)Memory + speed win. No __dict__.
@dataclass(kw_only=True)Force keyword args. Robust against field reordering.
field(default_factory=list)Mutable default. Required for list / dict.
field(init=False, default=…)Computed field, not part of __init__.
asdict(obj), astuple(obj)Recursive conversion.
replace(obj, name="x")Copy-with-overrides. Safe for frozen.
python
from dataclasses import dataclass, field, asdict, replace
from typing import Self

@dataclass(slots=True, frozen=True, kw_only=True)
class User:
    id: int
    name: str
    email: str
    tags: list[str] = field(default_factory=list)

    @classmethod
    def from_row(cls, row: dict) -> Self:
        return cls(id=row["id"], name=row["name"], email=row["email"])

u = User(id=1, name="Ada", email="ada@example.com")
asdict(u)                       # {'id': 1, 'name': 'Ada', ...}
u2 = replace(u, name="Ada L.")  # frozen-safe copy with overrides
hash(u)                         # works because frozen=True

asyncio · concurrencyAsync

async def fn(): …Coroutine. Call returns a coroutine, not a value.
asyncio.run(coro)Sync entry-point. Use once per program.
async with asyncio.TaskGroup() as tg:Preferred Structured concurrency. Exceptions propagate.
await asyncio.gather(*coros)Legacy Pre-3.11 spawn. No grouping.
asyncio.timeout(s)async with asyncio.timeout(5): — cancels child tasks.
asyncio.Semaphore(N)Bound concurrency. Use around await fetch(…).
asyncio.Queue / Event / LockPrimitives. Avoid sharing mutable state without one.
async for x in stream:Iterate async iterators.
async with cm:Async context managers (DB sessions, httpx clients).
asyncio.to_thread(fn, *a)Run a sync function in a thread without blocking the loop.
python
import asyncio
import httpx

async def fetch(client: httpx.AsyncClient, url: str) -> int:
    r = await client.get(url, timeout=5.0)
    return r.status_code

async def main(urls: list[str]) -> list[int]:
    async with httpx.AsyncClient() as client:
        # asyncio.TaskGroup (3.11+) — structured concurrency
        async with asyncio.TaskGroup() as tg:
            tasks = [tg.create_task(fetch(client, u)) for u in urls]
        return [t.result() for t in tasks]

# Run from a sync entry point
results = asyncio.run(main(["https://example.com", "https://example.org"]))

# Bound concurrency with a Semaphore
sem = asyncio.Semaphore(8)

async def bounded(client, url):
    async with sem:
        return await fetch(client, url)

paths as objectsPathlib

Path("a/b") / "c.txt"Compose with /.
p.exists(), p.is_file(), p.is_dir()Predicates.
p.read_text(encoding="utf-8")Whole-file read.
p.write_text(s, encoding="utf-8")Whole-file write.
p.write_bytes(b)Binary write.
p.mkdir(parents=True, exist_ok=True)Idempotent directory creation.
p.glob("*.json") / p.rglob("*.json")Top-level / recursive globs.
p.stem, p.suffix, p.name, p.parentPath parts.
p.with_suffix(".csv")Swap extensions.
p.relative_to(root)Make a path relative.
p.resolve()Absolute, with symlinks resolved.
python
from pathlib import Path

p = Path("data/inputs/log.txt")
p.parent.mkdir(parents=True, exist_ok=True)

p.write_text("hello\n", encoding="utf-8")
content = p.read_text(encoding="utf-8")

# Glob / iterate
for f in Path("data").rglob("*.json"):
    print(f.relative_to("data"))

# Compose paths
out = Path.home() / "exports" / f"{p.stem}.csv"

# Inspect
p.exists(), p.is_file(), p.suffix, p.stem, p.name

# Streaming line read
with p.open(encoding="utf-8") as fh:
    for line in fh:
        ...

combinators · cachesItertools & functools

itertools

chain(a, b)Concatenate iterables lazily.
chain.from_iterable(iters)Flatten one level.
pairwise(seq)Sliding window of 2. (3.10+)
groupby(seq, key=…)Consecutive-key grouping. Sort first if needed.
accumulate(seq, fn)Running totals / scans.
batched(seq, n)Yield N-sized chunks (3.12+).
islice(seq, start, stop)Slice an iterator without materialising.
product / combinations / permutationsCombinatorics.

functools

@cacheUnbounded memoisation. Pure functions only.
@lru_cache(maxsize=1024)Bounded memoisation. Cheap to add, easy to undo.
@cached_propertyLazy attribute, computed once per instance.
partial(fn, x=1)Pre-bind args. Useful for callbacks.
reduce(fn, seq, init)Fold. Most loops are clearer.
@singledispatchFunction overloads by first-arg type.

resource handlingContext managers

with open(p) as fh:Most common cm. Closes on exit.
with suppress(FileNotFoundError):Quietly swallow specific exceptions.
@contextmanagerDefine a cm from a generator. Yield in the middle.
@asynccontextmanagerAsync version.
ExitStack()Dynamic stack of context managers. Closes in reverse order.
redirect_stdout(io.StringIO())Capture stdout in tests.
tempfile.TemporaryDirectory()Scratch dir auto-cleaned on exit.
python
from contextlib import contextmanager, asynccontextmanager, ExitStack
import time

@contextmanager
def timed(label: str):
    t0 = time.perf_counter()
    try:
        yield
    finally:
        print(f"{label}: {time.perf_counter() - t0:.3f}s")

with timed("query"):
    result = run_query()

# Open many files; close them all even if one open() raises
with ExitStack() as stack:
    files = [stack.enter_context(open(p)) for p in paths]
    ...

# Async variant — perfect for httpx / DB sessions
@asynccontextmanager
async def session():
    s = await open_session()
    try:
        yield s
    finally:
        await s.close()

wrap behaviourDecorators

@functools.wraps(fn)Always use it in your own decorators. Preserves __name__ + docstring.
@staticmethod / @classmethodBound or not. classmethod takes cls.
@propertyComputed attribute. Pair with @x.setter if writable.
@cache · @lru_cacheMemoise. Pure functions only.
@contextmanagerMake a generator into a with context.
@dataclassAuto-generates dunders.
@override (3.12+)Mark a method as overriding a base. Type checker enforces.
A decorator with arguments is a decorator that returns a decorator. Easy mistake: forgetting the extra wrapper layer. Use functools.wraps inside the innermost function.

exceptions & groupsErrors

raise Foo("msg") from excChain causes. from None to hide.
try / except / else / finallyelse runs only if no exception. Useful for tight try blocks.
except (A, B) as e:Catch multiple types.
except* GroupError:Match members of an ExceptionGroup (3.11+).
raise ExceptionGroup("all failed", [a, b])Aggregate concurrent failures.
assert cond, "why"Debug check. Stripped under python -O; never use for validation.
contextlib.suppress(KeyError)Compact alternative to try/except/pass.

structural pattern matchingMatch

case 200:Literal match.
case [head, *tail]:Sequence with capture.
case {"type": "text", "value": v}:Mapping match.
case User(id=int() as uid, name=str() as name):Class match with type guards.
case _ if x > 0:Guard clause.
case _:Wildcard. Always include unless every case is exhaustive.
Reach for match on shape, not on equality. If you’d write elif x == 1 / x == 2 / …, a dict dispatch is clearer. match shines when you’re destructuring nested data.

async · pydantic · csv · pathlibEnd-to-end · Parallel GitHub fetcher

Fetch repos in parallel, validate with Pydantic, write a CSV. Wires async + structured concurrency + pathlib + csv in one runnable script.

python
# Fetch JSON from N endpoints in parallel, validate, write a CSV summary.
import asyncio, csv
from pathlib import Path
import httpx
from pydantic import BaseModel, HttpUrl, ValidationError

class Repo(BaseModel):
    full_name: str
    stargazers_count: int
    html_url: HttpUrl

async def fetch_repo(client: httpx.AsyncClient, name: str) -> Repo | None:
    r = await client.get(f"https://api.github.com/repos/{name}", timeout=10)
    if r.status_code != 200:
        return None
    try:
        return Repo.model_validate(r.json())
    except ValidationError:
        return None

async def main(repos: list[str], out: Path) -> None:
    async with httpx.AsyncClient(headers={"Accept": "application/vnd.github+json"}) as c:
        async with asyncio.TaskGroup() as tg:
            tasks = [tg.create_task(fetch_repo(c, r)) for r in repos]
    rows = [t.result() for t in tasks if t.result()]
    out.parent.mkdir(parents=True, exist_ok=True)
    with out.open("w", newline="", encoding="utf-8") as fh:
        w = csv.writer(fh)
        w.writerow(["repo", "stars", "url"])
        for r in rows:
            w.writerow([r.full_name, r.stargazers_count, str(r.html_url)])

if __name__ == "__main__":
    asyncio.run(main(
        ["python/cpython", "django/django", "pallets/flask"],
        Path("out/repos.csv"),
    ))

Best practiceGood to know

Prefer TaskGroup over gather. Structured concurrency cancels siblings on failure and surfaces all errors as an ExceptionGroup. gather swallows them silently unless you remember return_exceptions=False.
Use slots=True on hot-path dataclasses. Big memory + attribute-access win, almost no downside. Skip it only if you need to monkey-patch instances.
Run modules with python -m. python -m pkg.cli sets sys.path correctly and respects relative imports; calling pkg/cli.py directly often doesn’t.

Common trapsWatch out for

Mutable default arguments are evaluated once. def f(x=[]): shares the list across calls. Use x: list | None = None and x = x or [] inside.
Late-binding closures. [lambda: i for i in range(3)] all print 2. Capture with a default: lambda i=i: i.
Don’t mix asyncio with blocking calls. Calling sync requests.get inside an async handler stalls the event loop. Use httpx/aiohttp, or wrap with asyncio.to_thread.

Go deeperSee also

Python FAQ

What are Python comprehensions?

Comprehensions are compact expressions for building lists, dicts, sets, and generators. A list comprehension [x*2 for x in range(10) if x%2==0] replaces a for loop with an append. Dict comprehensions use {k: v for k, v in items}, set comprehensions {x for x in seq}, and generator expressions (x for x in seq) are lazy. Prefer comprehensions when the transformation is simple and readable.

What are Python dataclasses?

A dataclass (@dataclass decorator from the dataclasses module) auto-generates __init__, __repr__, and __eq__ from annotated class fields. Add frozen=True for immutable instances, slots=True (3.10+) for memory efficiency, or order=True to generate comparison methods. They are the modern alternative to named tuples and plain dicts for structured data.

How does async/await work in Python?

async def defines a coroutine; await suspends it until the awaited object completes, yielding control back to the event loop. asyncio.run(main()) starts the loop. Use asyncio.gather() to run coroutines concurrently, asyncio.create_task() to schedule without blocking, and async for / async with for async iterators and context managers.

What is the Python type system?

Python's type system is optional and enforced by external tools (mypy, Pyright, Ruff), not the runtime. Annotate variables and function signatures with types like str, list[int], dict[str, Any], Optional[X] (or X | None in 3.10+), and Union. Generic types use TypeVar; TypedDict annotates dict shapes. PEP 695 (3.12+) adds type X = ... aliases.

What is pathlib and when should I use it?

pathlib.Path is the modern API for filesystem operations — it replaces os.path, open(), and os calls with a readable object-oriented interface. Path('data') / 'file.txt' builds paths; path.read_text(), path.write_bytes(), path.glob('*.csv') cover most I/O needs. Use pathlib for all new code; os.path still works but is less readable.

Is Python free and open source?

Yes. Python is governed by the PSF (Python Software Foundation) under the Python Software Foundation License, which is OSI-approved and free for commercial use. CPython (the reference implementation) is free to download and ship. PyPI (the package index) is also free to publish to and install from.