Python

Syntax Basics

Indentation (4 spaces, no tabs) defines blocks — no braces. Statements end at the newline; use \ or wrap in () to continue.

x = 1
y = 2
if x < y:
    print("x is smaller")
elif x == y:
    print("equal")
else:
    print("x is bigger")

# multi-line via implicit continuation inside brackets
total = (
    1 + 2
    + 3
)

Variables are names bound to objects — no declarations, no fixed type. _ is a throwaway name (for _ in range(3):).

Data Types

TypeMutableExample
int, float, complexno42, 3.14, 1+2j
strno"hi"
boolnoTrue, False
listyes[1, 2, 3]
tupleno(1, 2, 3)
dictyes{"a": 1}
set / frozensetyes / no{1, 2}
NoneType—None
a, b = 1, 2          # tuple unpacking
a, b = b, a           # swap
nums = [1, 2, 3]
nums[-1]               # 3 (negative index)
nums[1:]                # [2, 3] (slice)
"abc"[::-1]              # "cba" (reverse via slice)
len(nums), sum(nums), max(nums)

Truthiness: 0, 0.0, "", [], {}, set(), None are falsy; everything else is truthy.

Control Flow

for i, val in enumerate(["a", "b"]):
    print(i, val)

for k, v in {"a": 1}.items():
    print(k, v)

while True:
    if done:
        break
    continue  # skip to next iteration

# ternary
label = "even" if n % 2 == 0 else "odd"

# match statement (3.10+)
match status:
    case 200:
        print("ok")
    case 404 | 500:
        print("error")
    case _:
        print("unknown")

Functions

def greet(name, greeting="Hello", *args, **kwargs):
    return f"{greeting}, {name}!"

greet("Ana")
greet("Ana", greeting="Hi")

# keyword-only / positional-only (3.8+)
def f(a, b, /, c, *, d):
    ...  # a,b positional-only; d keyword-only

square = lambda x: x * x  # anonymous function, use sparingly

def outer():
    count = 0
    def inner():
        nonlocal count
        count += 1
        return count
    return inner  # closure

*args collects extra positional args into a tuple; **kwargs collects extra keyword args into a dict.

Comprehensions

squares = [x * x for x in range(10) if x % 2 == 0]
lookup = {x: x * x for x in range(5)}
uniques = {x % 3 for x in range(10)}
gen = (x * x for x in range(10))  # lazy generator expression

# nested
pairs = [(x, y) for x in range(3) for y in range(3) if x != y]

Prefer a comprehension over a for loop + append when the transform is a single expression — falls back to a loop once logic branches.

Classes And OOP

class Animal:
    kingdom = "Animalia"  # class attribute

    def __init__(self, name):
        self.name = name  # instance attribute

    def speak(self) -> str:
        raise NotImplementedError

    def __repr__(self):
        return f"Animal({self.name!r})"

class Dog(Animal):
    def speak(self) -> str:
        return f"{self.name} says Woof"

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int  # auto __init__, __repr__, __eq__

Dunder methods (__init__, __repr__, __eq__, __len__, __iter__) hook into built-in operators. super().__init__() calls the parent constructor.

Error Handling

try:
    result = 10 / n
except ZeroDivisionError as e:
    print(f"cannot divide: {e}")
except (TypeError, ValueError):
    print("bad input")
else:
    print("ran when no exception")
finally:
    print("always runs")

class InsufficientFundsError(Exception):
    pass

raise InsufficientFundsError("balance too low")

# context manager — guarantees cleanup
with open("file.txt") as f:
    data = f.read()

Catch the narrowest exception type that makes sense; avoid bare except:.

File IO

with open("data.txt", "r") as f:
    for line in f:
        print(line.strip())

with open("out.txt", "w") as f:
    f.write("hello\n")

import json
with open("data.json") as f:
    obj = json.load(f)
json.dumps(obj, indent=2)

from pathlib import Path
p = Path("data") / "file.txt"
p.exists(), p.read_text(), p.parent

Prefer pathlib.Path over os.path string joining for new code.

Stdlib Essentials

ModuleUse
os, pathlibfilesystem, env vars
sysargv, exit, stdio
collectionsCounter, defaultdict, deque, namedtuple
itertoolschain, product, groupby, combinations
functoolsreduce, lru_cache, partial
datetimedates/times
reregex
json / csvserialization
subprocessrun external commands
argparseCLI parsing
from collections import Counter, defaultdict
Counter("mississippi").most_common(2)  # [('i', 4), ('s', 4)]
d = defaultdict(list)
d["missing"].append(1)  # no KeyError

from functools import lru_cache
@lru_cache(maxsize=None)
def fib(n): return n if n < 2 else fib(n - 1) + fib(n - 2)

Type Hints

def add(a: int, b: int) -> int:
    return a + b

from typing import Optional, Union

def find(id_: int) -> Optional[str]: ...
def parse(v: Union[int, str]) -> int: ...

# 3.10+ union syntax
def parse2(v: int | str) -> int: ...

names: list[str] = []
scores: dict[str, int] = {}

Hints aren’t enforced at runtime — use mypy or pyright to check statically.

Virtual Envs And Packaging

python -m venv .venv
source .venv/bin/activate      # Windows: .venv\Scripts\activate
pip install requests
pip freeze > requirements.txt
pip install -r requirements.txt
deactivate

pyproject.toml is the modern packaging manifest (replaces setup.py for most projects). Tools like uv, poetry, and pip-tools manage dependency resolution and lockfiles on top of it.

Common Idioms

# EAFP over LBYL
try:
    value = d["key"]
except KeyError:
    value = None

# unpacking with star
first, *rest = [1, 2, 3, 4]

# chained comparisons
if 0 < x < 10: ...

# walrus operator (3.8+)
if (n := len(data)) > 10:
    print(f"{n} items")

# f-strings with debug spec (3.8+)
print(f"{value=}")  # prints "value=..."