pip install "psycopg[binary]" # prebuilt libpq pip install "psycopg[binary,pool]" # + connection pool★Thebinaryextra ships a compiled libpq — no system Postgres headers needed. Plainpsycopglinks a system libpq.import psycopg with psycopg.connect("dbname=app user=me host=localhost") as conn: with conn.cursor() as cur: cur.execute("SELECT 1"); cur.fetchone()★The canonical shape. Conninfo is a keyword string or a URL"postgresql://me@localhost/app". Exiting thewithcommits AND closes the connection (card 4).row = conn.execute("SELECT * FROM t WHERE id=%s", (1,)).fetchone()★conn.execute()shortcut (new in v3) creates a temporary cursor — skip the explicitcursor()for one-off queries.
cur.execute("SELECT * FROM t WHERE id=%s AND name=%s", (id, name))★Placeholders are%sfor every type (never%d, never?). Pass a tuple of values — psycopg adapts and quotes them safely.cur.execute("... WHERE name=%(name)s", {"name": n})★Named placeholders%(name)stake a dict — clearer with many params.cur.executemany("INSERT INTO t(a) VALUES(%s)", [(1,), (2,), (3,)])★Batch the same statement over many param sets. For very large loads, prefer COPY (card 7).cur.execute(t"SELECT * FROM t WHERE id={id}")3.3 + Py3.14 t-stringsPsycopg 3.3 accepts template strings: interpolated values become parameters, not raw text — safe by construction. Needs Python 3.14 t-strings.
cur.fetchone() · cur.fetchall() · cur.fetchmany(100) for record in cur: # cursors are iterable ...★Default rows are tuples. Iterate the cursor to stream results lazily.from psycopg.rows import dict_row with psycopg.connect(conninfo, row_factory=dict_row) as conn: conn.execute("SELECT ...").fetchone()["name"]★Row factories change row shape:dict_row,namedtuple_row,class_row(MyModel). Set per-connection or per-cursor.with conn.cursor(name="big") as cur: # server-side cursor cur.execute("SELECT * FROM huge") for row in cur: ...Named = server-side cursor: Postgres streams rows in batches instead of buffering the whole result client-side. Essential for large tables.