Python Cheatsheet

Standard Library

Use this Python reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

collections — Specialized Containers

from collections import (
    Counter, defaultdict, OrderedDict, ChainMap,
    namedtuple, deque, UserDict, UserList, UserString
)

# Counter
c = Counter("abracadabra")       # {'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1}
c.most_common(3)                 # top 3
c.update("aab")                  # add more counts
Counter("abc") + Counter("aab")  # combine

# defaultdict
from collections import defaultdict
groups = defaultdict(list)
for item in data:
    groups[item.key].append(item)

# deque
q = deque(maxlen=10)     # fixed-size ring buffer
q.append(1)              # right
q.appendleft(0)          # left
q.pop()                  # right
q.popleft()              # left  O(1) unlike list
q.rotate(2)              # rotate right 2; negative = left

# namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(1, 2); p.x; p._asdict(); p._replace(x=10)

# ChainMap
merged = ChainMap(user_config, defaults)  # first wins on lookup

itertools — Iterator Building Blocks

from itertools import (
    count, cycle, repeat,
    accumulate, chain, compress, dropwhile, filterfalse,
    islice, pairwise, starmap, takewhile, zip_longest,
    product, permutations, combinations, combinations_with_replacement,
    groupby
)

# Infinite iterators
count(10)          # 10 11 12 13 ...
count(1, 0.5)      # 1.0 1.5 2.0 ...
cycle([1, 2, 3])   # 1 2 3 1 2 3 ...
repeat(42)         # 42 42 42 ...
repeat(42, 5)      # 42 five times

# Finite iterators
list(accumulate([1, 2, 3, 4]))          # [1, 3, 6, 10] (running sum)
list(accumulate([1, 2, 3], lambda a, b: a * b))  # [1, 2, 6]
list(chain([1, 2], [3, 4], [5]))        # [1, 2, 3, 4, 5]
list(chain.from_iterable([[1,2],[3,4]]))# [1, 2, 3, 4]
list(compress("ABCDE", [1,0,1,0,1]))   # ['A', 'C', 'E']
list(dropwhile(lambda x: x<5, [1,4,6,4,1]))  # [6, 4, 1]
list(takewhile(lambda x: x<5, [1,4,6,4,1]))  # [1, 4]
list(islice(count(10), 5))             # [10, 11, 12, 13, 14]
list(filterfalse(lambda x: x%2, range(10)))   # [0, 2, 4, 6, 8]
list(pairwise([1, 2, 3, 4]))           # [(1,2),(2,3),(3,4)]  Python 3.10+
list(starmap(pow, [(2,3),(3,2)]))      # [8, 9]
list(zip_longest("ABC", "xy", fillvalue="-"))  # [('A','x'),('B','y'),('C','-')]

# Combinatoric iterators
list(product("AB", repeat=2))          # [('A','A'),('A','B'),('B','A'),('B','B')]
list(permutations("ABC", 2))           # all 2-length permutations
list(combinations("ABC", 2))           # [('A','B'),('A','C'),('B','C')]
list(combinations_with_replacement("AB", 2))  # [('A','A'),('A','B'),('B','B')]

# groupby — consecutive equal keys (sort first!)
data = sorted([{"t": "A", "v": 1}, {"t": "B", "v": 2}, {"t": "A", "v": 3}], key=lambda x: x["t"])
for key, group in groupby(data, key=lambda x: x["t"]):
    print(key, list(group))

functools — Functional Tools

from functools import (
    reduce, partial, partialmethod,
    lru_cache, cache, cached_property,
    wraps, total_ordering, singledispatch,
    cmp_to_key
)

reduce(lambda a, b: a + b, [1,2,3,4])  # 10

# Partial — fix some arguments
def power(base, exp): return base ** exp
square = partial(power, exp=2)
square(5)   # 25

# singledispatch — function overloading by type
@singledispatch
def process(arg):
    raise TypeError(f"Unsupported type: {type(arg)}")

@process.register(int)
def _(arg): return arg * 2

@process.register(str)
def _(arg): return arg.upper()

process(5)      # 10
process("hi")   # "HI"

# cmp_to_key — use old-style comparison function with sort
import functools
functools.cmp_to_key(lambda a, b: a - b)

math — Math Functions

import math

math.sqrt(16)         # 4.0
math.isqrt(16)        # 4  (integer sqrt, Python 3.8+)
math.pow(2, 10)       # 1024.0 (always float, unlike built-in pow)
math.exp(1)           # e
math.log(100, 10)     # 2.0
math.log2(8)          # 3.0
math.log10(1000)      # 3.0
math.log1p(x)         # log(1+x) accurate for small x
math.floor(3.7)       # 3
math.ceil(3.2)        # 4
math.trunc(3.9)       # 3
math.fabs(-3.2)       # 3.2 (float absolute value)
math.factorial(10)    # 3628800
math.gcd(12, 8)       # 4
math.lcm(4, 6)        # 12  (Python 3.9+)
math.comb(10, 3)      # 120
math.perm(10, 3)      # 720
math.prod([1,2,3,4])  # 24  (Python 3.8+)
math.fsum([0.1]*10)   # 1.0 (exact floating-point sum)
math.hypot(3, 4)      # 5.0
math.atan2(y, x)      # angle in radians
math.degrees(math.pi) # 180.0
math.radians(180)     # math.pi
math.sin(math.pi/2)   # 1.0
math.cos(0)           # 1.0
math.tan(math.pi/4)   # ~1.0
math.inf              # float infinity
math.nan              # NaN
math.e                # 2.718...
math.pi               # 3.14159...
math.tau              # 6.28318... (2π)
math.isinf(math.inf)  # True
math.isnan(math.nan)  # True
math.isfinite(1.0)    # True
math.isclose(0.1+0.2, 0.3, rel_tol=1e-9, abs_tol=1e-12)  # True

random — Random Numbers

import random

random.random()                     # float in [0.0, 1.0)
random.uniform(1.0, 10.0)          # float in [a, b]
random.randint(1, 6)               # int in [a, b] inclusive
random.randrange(0, 100, 2)        # like range() but random
random.gauss(mu=0, sigma=1)        # Gaussian distribution
random.normalvariate(mu=0, sigma=1)

random.choice([1, 2, 3])           # random element
random.choices([1,2,3], weights=[1,2,3], k=5)  # with replacement, weighted
random.sample([1,2,3,4,5], 3)      # without replacement
random.shuffle(lst)                 # in-place shuffle
random.seed(42)                     # reproducible results

# Cryptographically secure
import secrets
secrets.randbelow(100)              # [0, n)
secrets.choice([1,2,3])
secrets.token_bytes(32)            # random bytes
secrets.token_hex(16)              # hex string (32 chars)
secrets.token_urlsafe(16)          # URL-safe base64

datetime — Dates and Times

from datetime import date, time, datetime, timedelta, timezone

# Create
d = date(2024, 1, 15)
t = time(14, 30, 0)
dt = datetime(2024, 1, 15, 14, 30, 0)
dt = datetime.now()              # local time
dt = datetime.utcnow()           # UTC (deprecated in 3.12 — use below)
dt = datetime.now(timezone.utc)  # timezone-aware UTC

# From string
dt = datetime.fromisoformat("2024-01-15T14:30:00")
dt = datetime.strptime("15/01/2024", "%d/%m/%Y")

# To string
dt.isoformat()                   # "2024-01-15T14:30:00"
dt.strftime("%Y-%m-%d %H:%M:%S") # "2024-01-15 14:30:00"
dt.strftime("%B %d, %Y")         # "January 15, 2024"

# Attributes
dt.year, dt.month, dt.day
dt.hour, dt.minute, dt.second, dt.microsecond
dt.weekday()     # 0=Monday, 6=Sunday
dt.isoweekday()  # 1=Monday, 7=Sunday
dt.date()        # date part
dt.time()        # time part

# Arithmetic
delta = timedelta(days=7, hours=2, minutes=30)
dt + delta
dt - delta
dt2 - dt1       # → timedelta
delta.days, delta.seconds, delta.total_seconds()

# Timestamps
dt.timestamp()              # Unix timestamp (float)
datetime.fromtimestamp(ts)  # from Unix timestamp (local)
datetime.fromtimestamp(ts, tz=timezone.utc)  # UTC

# Timezone
from datetime import timezone, timedelta as td
utc = timezone.utc
est = timezone(td(hours=-5))
dt_utc = datetime.now(utc)
dt_est = dt_utc.astimezone(est)

# zoneinfo (Python 3.9+) — IANA tz database
from zoneinfo import ZoneInfo
tz = ZoneInfo("America/New_York")
dt = datetime.now(tz)

os and sys

import os, sys

# Process / environment
os.getpid()                  # process ID
os.getenv("HOME")            # env variable (None if missing)
os.environ["HOME"]           # env variable (KeyError if missing)
os.environ.get("VAR", "default")
os.putenv("VAR", "value")    # prefer os.environ["VAR"] = "value"

# System info
sys.platform       # "linux", "darwin", "win32"
sys.version        # "3.12.0 ..."
sys.version_info   # sys.version_info(major=3, minor=12, ...)
sys.argv           # ["script.py", "arg1", "arg2"]
sys.exit(0)        # exit with code (0 = success)
sys.stdin, sys.stdout, sys.stderr

# Paths
sys.path           # module search path
sys.prefix         # Python installation directory

# Recursion
sys.getrecursionlimit()      # default 1000
sys.setrecursionlimit(5000)

# Sizes
sys.getsizeof(obj)   # object memory in bytes (shallow)

re — Regular Expressions

import re

re.match(r"\d+", text)           # match at start
re.search(r"\d+", text)          # first match anywhere
re.findall(r"\d+", text)         # list of all matches
re.finditer(r"\d+", text)        # iterator of match objects
re.sub(r"\d+", "N", text)        # replace all
re.sub(r"\d+", "N", text, count=1)  # replace first only
re.subn(r"\d+", "N", text)       # (new_str, count)
re.split(r"\s+", text)           # split
re.fullmatch(r"\d{3}", "123")    # entire string must match

# Match object
m = re.search(r"(\w+)\s(\w+)", text)
m.group(0)         # full match
m.group(1)         # first group
m.groups()         # all groups
m.start(1)         # start of group 1
m.span()           # (start, end) of full match

# Compile for reuse
pattern = re.compile(r"\d+", re.IGNORECASE)
pattern.search(text)

# Common flags
re.IGNORECASE  # re.I
re.MULTILINE   # re.M  — ^ and $ match line boundaries
re.DOTALL      # re.S  — . matches \n too
re.VERBOSE     # re.X  — allow whitespace and comments in pattern

json — JSON Serialization

import json

# Serialize
json.dumps(obj)                          # to string
json.dumps(obj, indent=2)               # pretty
json.dumps(obj, sort_keys=True)
json.dumps(obj, ensure_ascii=False)      # allow non-ASCII

json.dump(obj, file_obj)                 # to file

# Deserialize
obj = json.loads(s)                      # from string
obj = json.load(file_obj)               # from file

# Custom types
class DateEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

json.dumps({"dt": datetime.now()}, cls=DateEncoder)

# Custom decoder
def date_hook(d):
    for k, v in d.items():
        try:
            d[k] = datetime.fromisoformat(v)
        except (TypeError, ValueError):
            pass
    return d

json.loads(s, object_hook=date_hook)

pathlib — Paths

from pathlib import Path

p = Path.home() / ".config" / "app.json"
p.exists(); p.is_file(); p.is_dir()
p.read_text(); p.write_text("data")
p.read_bytes(); p.write_bytes(b"data")
p.mkdir(parents=True, exist_ok=True)
p.unlink(missing_ok=True)
list(p.parent.glob("*.json"))
list(p.parent.rglob("*.py"))
p.rename(new); p.replace(new)
p.stem; p.suffix; p.name; p.parent
str(p); p.as_posix()

subprocess — Run External Commands

import subprocess

# Simple — capture output
result = subprocess.run(["ls", "-la"], capture_output=True, text=True)
result.returncode    # 0 = success
result.stdout        # captured stdout
result.stderr        # captured stderr
result.check_returncode()   # raises CalledProcessError if non-zero

# check=True raises CalledProcessError automatically
result = subprocess.run(["git", "status"], check=True, capture_output=True, text=True)

# Shell command (security risk with untrusted input)
subprocess.run("ls -la | grep py", shell=True, check=True)

# Stream output to console (no capture)
subprocess.run(["python", "script.py"])

# With input
result = subprocess.run(["cat"], input="hello\n", capture_output=True, text=True)
result.stdout    # "hello\n"

# Get just the output
output = subprocess.check_output(["git", "log", "--oneline"], text=True)

threading and multiprocessing

import threading

def worker(n):
    print(f"thread {n}")

t = threading.Thread(target=worker, args=(1,), daemon=True)
t.start()
t.join()               # wait for thread to finish
t.is_alive()

# Lock
lock = threading.Lock()
with lock:
    shared_resource += 1

# Other sync primitives
threading.RLock()        # reentrant lock
threading.Event()        # event.set(), event.wait()
threading.Semaphore(5)   # limit concurrent access
threading.Barrier(3)     # wait until N threads reach barrier

# Thread pool
from concurrent.futures import ThreadPoolExecutor, as_completed

with ThreadPoolExecutor(max_workers=4) as executor:
    futures = [executor.submit(worker, i) for i in range(10)]
    for future in as_completed(futures):
        result = future.result()    # raises if worker raised

# Process pool (for CPU-bound work, bypasses GIL)
from concurrent.futures import ProcessPoolExecutor

with ProcessPoolExecutor(max_workers=4) as executor:
    results = list(executor.map(cpu_task, data))

typing — Type Hints

from typing import (
    Any, Union, Optional, Literal,
    List, Dict, Set, Tuple, FrozenSet,
    Sequence, Mapping, MutableMapping, Iterable, Iterator, Generator,
    Callable, TypeVar, Generic, Protocol,
    ClassVar, Final, TypedDict, NamedTuple,
    cast, overload, TYPE_CHECKING
)

# Modern syntax (Python 3.9+) — use built-in generics instead of typing
def f(x: list[int]) -> dict[str, int]: ...
def g(x: tuple[int, str, float]): ...

# Optional and Union
def h(x: int | None) -> str | None: ...   # Python 3.10+
def h(x: Optional[int]) -> Optional[str]: ...  # older style

# Callable
def apply(fn: Callable[[int, str], bool]) -> None: ...

# TypeVar
T = TypeVar("T")
def identity(x: T) -> T: return x

# Protocol (structural subtyping)
from typing import Protocol
class Sized(Protocol):
    def __len__(self) -> int: ...

# TypedDict
class Movie(TypedDict):
    title: str
    year: int
    rating: float

# TYPE_CHECKING — avoid circular imports at runtime
if TYPE_CHECKING:
    from mymodule import HeavyType

dataclasses — Data Classes

from dataclasses import dataclass, field, asdict, astuple, replace

@dataclass
class Point:
    x: float
    y: float
    z: float = 0.0
    _cache: dict = field(default_factory=dict, repr=False, init=False)

p = Point(1.0, 2.0)
asdict(p)         # {'x': 1.0, 'y': 2.0, 'z': 0.0}
astuple(p)        # (1.0, 2.0, 0.0)
replace(p, x=10)  # new instance with x=10

@dataclass(frozen=True)    # immutable + hashable
@dataclass(order=True)     # comparison operators
@dataclass(slots=True)     # __slots__ (Python 3.10+)

contextlib — Context Manager Utilities

from contextlib import (
    contextmanager, asynccontextmanager,
    suppress, nullcontext, redirect_stdout,
    ExitStack, closing
)

@contextmanager
def managed():
    setup()
    try:
        yield resource
    finally:
        teardown()

with suppress(FileNotFoundError, PermissionError):
    os.remove("temp.txt")

with redirect_stdout(open("out.txt", "w")):
    print("goes to file")

# ExitStack — dynamically nest context managers
with ExitStack() as stack:
    files = [stack.enter_context(open(f)) for f in filenames]

enum — Enumerations

from enum import Enum, IntEnum, Flag, auto

class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

Color.RED          # <Color.RED: 1>
Color.RED.name     # "RED"
Color.RED.value    # 1
Color(1)           # Color.RED
Color["RED"]       # Color.RED
list(Color)        # [<Color.RED: 1>, ...]

# auto() — automatic values
class Direction(Enum):
    NORTH = auto()   # 1
    SOUTH = auto()   # 2

# IntEnum — also an int
class Status(IntEnum):
    OK = 200
    NOT_FOUND = 404

Status.OK == 200   # True

# Flag — bitwise combination
class Permission(Flag):
    READ = auto()
    WRITE = auto()
    EXEC = auto()
    RWX = READ | WRITE | EXEC

perm = Permission.READ | Permission.WRITE
Permission.READ in perm   # True

logging — Logging

import logging

# Basic setup
logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler("app.log"),
    ]
)

# Log levels: DEBUG < INFO < WARNING < ERROR < CRITICAL
logging.debug("detailed info")
logging.info("general info")
logging.warning("something unexpected")
logging.error("an error occurred")
logging.critical("serious error")
logging.exception("error with traceback")  # includes exc_info

# Named loggers (preferred)
logger = logging.getLogger(__name__)
logger.info("message")
logger.error("error: %s", error_msg)   # lazy string formatting

# Structured logging
logger.info("user login", extra={"user_id": 42, "ip": "1.2.3.4"})

argparse — CLI Argument Parsing

import argparse

parser = argparse.ArgumentParser(description="My tool")
parser.add_argument("filename", help="input file")
parser.add_argument("-v", "--verbose", action="store_true")
parser.add_argument("-n", "--count", type=int, default=10)
parser.add_argument("-o", "--output", metavar="FILE")
parser.add_argument("--format", choices=["json", "csv", "text"])
parser.add_argument("--tags", nargs="+")     # one or more values
parser.add_argument("--flags", nargs="*")    # zero or more

args = parser.parse_args()
args.filename
args.verbose
args.count

struct — Binary Data

import struct

# Pack: format string → bytes
data = struct.pack(">IH", 1234, 567)   # big-endian uint32 + uint16

# Unpack: bytes → tuple
values = struct.unpack(">IH", data)    # (1234, 567)

# Format characters
# > big-endian, < little-endian, ! network (big), = native
# b  int8, B uint8, h int16, H uint16, i int32, I uint32
# l int32, L uint32, q int64, Q uint64, f float32, d float64
# c char (bytes of length 1), s bytes, x pad byte

struct.calcsize(">IH")   # 6 bytes

# Struct class for repeated use
s = struct.Struct(">IH")
s.pack(1, 2)
s.unpack(data)