Python#
Shortcut keys for the Python interactive interpreter. CPython on Linux / macOS uses GNU readline at the REPL, so the readline emacs keymap is in play. CPython 3.13+ ships a new PyREPL by default (the same line editor as PyPy) which adds multi-line editing, syntax highlighting, and an interactive history pager.
For richer interactive Python, IPython has its own bindings
(prompt_toolkit), and pdb / ipdb rebind a few keys for
debugger control. For setup, virtual environments, and packaging,
see Python.
Cursor Movement (REPL)#
Key |
Action |
|---|---|
|
move to start of line |
|
move to end of line |
|
move back one character |
|
move forward one character |
|
move back one word |
|
move forward one word |
Editing (REPL)#
Key |
Action |
|---|---|
|
delete character under cursor (EOF on empty line, exits) |
|
delete character before cursor |
|
delete word forward |
|
delete word backward |
|
kill to end of line |
|
kill to start of line |
|
yank (paste) last killed text |
|
transpose characters |
|
transpose words |
|
uppercase / lowercase / capitalize word |
|
undo |
History (REPL)#
CPython reads / writes ~/.python_history when readline is
linked. Configure with readline.read_history_file /
write_history_file in $PYTHONSTARTUP.
Key |
Action |
|---|---|
|
previous history entry |
|
next history entry |
|
incremental reverse search |
|
incremental forward search ( |
|
insert last argument of previous line |
|
inspect history at runtime |
|
clear current-session history |
PyREPL (3.13+)#
The new default interpreter in 3.13. Bindings overlap with the readline set; the additions are multi-line editing, paste mode, and a few discovery shortcuts.
Key |
Action |
|---|---|
|
enter REPL help / commands menu |
|
browse history with a pager |
|
toggle paste mode (multi-line paste without auto-indent) |
|
edit current multi-line buffer in |
|
move between lines in a multi-line block (not history) |
|
previous / next history block |
|
completion (rlcompleter) |
|
leave the REPL (no parens needed in PyREPL) |
Process Control#
Key |
Action |
|---|---|
|
raise |
|
send EOF (exits the REPL on empty line) |
|
clear screen |
|
suspend interpreter (SIGTSTP) |
|
send SIGQUIT (core dump) |
IPython#
IPython (and Jupyter terminal) replaces readline with
prompt_toolkit. Many bindings overlap, but the magic /
introspection helpers are IPython-only.
Key |
Action |
|---|---|
|
completion menu (paginated) |
|
inspect object at cursor (signature + docstring tip) |
|
print docstring (e.g. |
|
print source (e.g. |
|
reverse history search |
|
insert newline without submitting (multi-line) |
|
insert newline (alt binding) |
|
submit current cell (where the terminal supports it) |
|
cancel current line (does not exit IPython) |
|
exit IPython |
|
print session history (IPython magic) |
|
open buffer in |
|
paste from clipboard and execute |
|
paste a block with explicit terminator ( |
pdb / ipdb#
Debugger-specific single-letter commands (typed, then Enter).
Command |
Action |
|---|---|
|
help |
|
list around current line / list whole function |
|
next (step over) |
|
step (into function) |
|
return from current function |
|
continue until next breakpoint |
|
set breakpoint |
|
clear breakpoint |
|
print expression |
|
pretty-print expression |
|
print stack trace |
|
move up / down the stack |
|
quit debugger |
|
same as |
|
run an arbitrary Python statement in the current frame |
|
drop into IPython mid-frame (ipdb) |
Config and Discovery#
Command / file |
Action |
|---|---|
|
path to a file executed at REPL start (history, completion) |
|
default history file |
|
enable Tab completion (rlcompleter) |
|
module that powers Tab completion |
|
readline config (applies system-wide, not just Python) |
|
force the old REPL (disable PyREPL) in 3.13+ |
|
generate IPython config |
|
IPython config (key bindings, prompt, etc.) |
Read / Write Files#
from pathlib import Path
text = Path("input.txt").read_text(encoding="utf-8")
data = Path("input.json").read_text(encoding="utf-8")
Path("output.txt").write_text("hello\n", encoding="utf-8")
for line in Path("input.txt").read_text(encoding="utf-8").splitlines():
process(line)
# Iterating large files line-by-line
with open("big.txt", encoding="utf-8") as f:
for line in f:
process(line)
Read / Write JSON#
import json
from pathlib import Path
data = json.loads(Path("in.json").read_text())
Path("out.json").write_text(json.dumps(data, indent=2, ensure_ascii=False))
Walk a Directory#
from pathlib import Path
for p in Path(".").rglob("*.py"):
if p.is_file():
print(p)
# Filter by mtime
import time
cutoff = time.time() - 7 * 86400
recent = [p for p in Path(".").rglob("*") if p.stat().st_mtime > cutoff]
Dataclasses#
from dataclasses import dataclass, field
from datetime import datetime
@dataclass(slots=True, frozen=True)
class Person:
name: str
age: int = 0
tags: list[str] = field(default_factory=list)
created_at: datetime = field(default_factory=datetime.utcnow)
Group By Key#
from collections import defaultdict
by_team = defaultdict(list)
for u in users:
by_team[u.team].append(u)
Counter#
from collections import Counter
words = Counter(text.split())
words.most_common(10)
LRU Cache#
from functools import cache, lru_cache
@cache # unbounded; Python 3.9+
def fib(n: int) -> int:
return n if n < 2 else fib(n - 1) + fib(n - 2)
@lru_cache(maxsize=1024) # bounded
def lookup(key: str) -> str:
return expensive(key)
Context Manager#
from contextlib import contextmanager
import time
@contextmanager
def timed(label):
start = time.perf_counter()
try:
yield
finally:
print(f"{label}: {time.perf_counter() - start:.3f}s")
with timed("parse"):
parse(big_input)
Retry With Backoff#
import time
from typing import Callable, TypeVar
T = TypeVar("T")
def retry(fn: Callable[[], T], attempts: int = 5, base: float = 0.5) -> T:
last = None
for i in range(attempts):
try:
return fn()
except Exception as e:
last = e
time.sleep(base * 2 ** i)
raise last
Async HTTP With httpx#
import asyncio, httpx
async def fetch_all(urls: list[str]) -> list[str]:
async with httpx.AsyncClient(timeout=10) as c:
rs = await asyncio.gather(*(c.get(u) for u in urls))
return [r.text for r in rs]
Subprocess#
import subprocess
r = subprocess.run(
["git", "rev-parse", "HEAD"],
capture_output=True, text=True, check=True,
)
print(r.stdout.strip())
Argparse#
import argparse
from pathlib import Path
def main() -> None:
p = argparse.ArgumentParser()
p.add_argument("input", type=Path)
p.add_argument("--limit", type=int, default=100)
p.add_argument("--verbose", "-v", action="store_true")
args = p.parse_args()
run(args.input, args.limit, args.verbose)
Pydantic Settings#
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_prefix="APP_")
host: str = "0.0.0.0"
port: int = 8080
database_url: str
settings = Settings()
Logging#
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s %(message)s",
)
log = logging.getLogger(__name__)
log.info("starting")
Idiomatic Comprehensions#
Filter and map in one expression.
evens = [x * x for x in xs if x % 2 == 0]
Invert a dict in one expression.
inv = {v: k for k, v in d.items()}
Flatten a list of lists with itertools.chain.
from itertools import chain
flat = list(chain.from_iterable(lists))
Sliding-window pairs with itertools.pairwise (Python 3.10+).
from itertools import pairwise
diffs = [b - a for a, b in pairwise(xs)]