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
| Type | Mutable | Example |
|---|---|---|
int, float, complex | no | 42, 3.14, 1+2j |
str | no | "hi" |
bool | no | True, False |
list | yes | [1, 2, 3] |
tuple | no | (1, 2, 3) |
dict | yes | {"a": 1} |
set / frozenset | yes / 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
| Module | Use |
|---|---|
os, pathlib | filesystem, env vars |
sys | argv, exit, stdio |
collections | Counter, defaultdict, deque, namedtuple |
itertools | chain, product, groupby, combinations |
functools | reduce, lru_cache, partial |
datetime | dates/times |
re | regex |
json / csv | serialization |
subprocess | run external commands |
argparse | CLI 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=..."