A quick-reference for the most commonly used Python features and core data structures. Everything is an object, functions are first-class, and collections are expressive and battle-tested.
Python is dynamically and strongly typed. Variables are references to objects โ type hints provide editor diagnostics without enforcing types at runtime.
x = 42
name: str = "Alice" # type hint annotation
a, b = 1, 2 # multiple assignment
no keyword like var/let; reassignment changes object binding
i = 100 # int (arbitrary precision)
f = 3.14 # float (64-bit IEEE 754)
b = True # bool (subclass of int)
s = "text" # str (immutable Unicode)
raw = b"bytes" # bytes (immutable 8-bit bytes)
n = None # NoneType (singleton representing null)
type(x) # returns type object: <class 'int'>
isinstance(x, (int, float)) # True if x is int or float
x is None # identity check (compares memory address)
a == b # equality check (compares values)
always use is to compare against None or bool singletons
int("42") / float(10)
str(100) / bool(1) # 0, "", [], None are falsy; others truthy
list("abc") # ['a', 'b', 'c']
nums: list[int] = [] # empty list (list() also works)
scores: list[float] = [1.5, 2.0] # list literal
mapping: dict[str, int] = {} # empty dict (dict() also works)
mapping = dict(a=1, b=2) # from keyword args
unique: set[str] = set() # empty set โ {} creates a dict, not a set!
unique = {"apple", "banana"} # set literal with values
point: tuple[int, int] = (0, 0) # fixed-size, heterogeneous
path: tuple[str, ...] = ("a", "b", "c") # variable-length, homogeneous
single = (42,) # one-element tuple โ trailing comma required
Type hints (e.g. list[int]) are checked by mypy/pyright, not at runtime. list[int] syntax requires Python 3.9+; use List[int] from typing for earlier versions.
Always available without importing. Prefer them over manual loops โ implemented in C and communicate intent clearly.
print("hello", end="\n", sep=" ")
end and sep are optional kwargs
type(x)
isinstance(x, (int, float)) # accepts tuple of types
range(5) # 0, 1, 2, 3, 4 (stop only)
range(2, 7) # 2, 3, 4, 5, 6 (start, stop โ stop is exclusive)
range(0, 10, 2) # 0, 2, 4, 6, 8 (start, stop, step)
range(10, 0, -1) # 10, 9, 8, ..., 1 (count down)
list(range(3)) # [0, 1, 2] (convert to list)
fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits):
print(i, fruit) # 0 apple / 1 banana / 2 cherry
for i, fruit in enumerate(fruits, start=1):
print(i, fruit) # 1 apple / 2 banana / 3 cherry
list(enumerate(fruits)) # [(0,'apple'), (1,'banana'), (2,'cherry')]
yields (index, value) tuples; avoids a manual counter variable
names = ["Alice", "Bob", "Carol"]
scores = [95, 82, 78]
# pair elements from two (or more) iterables
list(zip(names, scores))
# [("Alice",95), ("Bob",82), ("Carol",78)] โ stops at shortest
for name, score in zip(names, scores):
print(f"{name}: {score}")
# build a dict from two parallel lists
d = dict(zip(names, scores))
# {"Alice": 95, "Bob": 82, "Carol": 78}
# unzip โ transpose a list of pairs
pairs = [("Alice", 95), ("Bob", 82)]
names2, scores2 = zip(*pairs)
# names2=("Alice","Bob") scores2=(95,82)
# pad shorter lists instead of stopping
from itertools import zip_longest
list(zip_longest([1, 2, 3], ["a", "b"], fillvalue="-"))
# [(1,'a'), (2,'b'), (3,'-')]
lazy โ wrap in list() to materialise. Use zip_longest when lists may differ in length.
nums = [1, 2, 3, 4, 5]
# map: apply a function to every element
list(map(str, nums)) # ['1','2','3','4','5']
list(map(lambda x: x**2, nums)) # [1, 4, 9, 16, 25]
# filter: keep elements where fn returns True
list(filter(lambda x: x % 2, nums)) # [1, 3, 5] โ odd numbers
list(filter(None, [0, 1, "", "ok"])) # [1, 'ok'] โ drops falsy values
# list comprehensions are usually more readable (equivalent):
[x**2 for x in nums] # same as map
[x for x in nums if x % 2] # same as filter
Both return lazy iterators โ wrap in list() to get a list.
nums = [3, 1, 4, 1, 5, 9]
sorted(nums) # [1, 1, 3, 4, 5, 9] โ new list
sorted(nums, reverse=True) # [9, 5, 4, 3, 1, 1]
words = ["banana", "fig", "apple"]
sorted(words, key=len) # ['fig', 'apple', 'banana'] โ by length
sorted(words, key=str.lower) # case-insensitive alphabetical
# list.sort() sorts in-place, returns None:
nums.sort() # mutates nums directly (no copy)
sorted() returns a new list; list.sort() mutates in-place and returns None.
nums = [1, 2, 3, 4, 0]
any(x > 3 for x in nums) # True โ at least one element > 3
all(x > 0 for x in nums) # False โ 0 fails the check
any([]) # False โ empty iterable
all([]) # True โ vacuously true
# useful for validation:
all(isinstance(x, int) for x in nums) # True โ all ints
any(x < 0 for x in nums) # False โ no negatives
any() short-circuits on first True; all() short-circuits on first False.
len(x) / min(x) / max(x) / sum(x)
min(x, key=fn) # min/max accept a key= kwarg, same as sorted
Python uses indentation for blocks. Loops support optional else clauses that run when the loop completes without a break.
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
else:
grade = "C"
status = "pass" if score >= 60 else "fail" # ternary
for i in range(5): # 0..4
print(i)
for item in container:
process(item)
count = 0
while count < 3:
count += 1
else:
print("completed without break")
else block executes only if while condition becomes false (not on break)
match command.split():
case ["quit"]: exit()
case ["load", filename]: load(filename)
case _: print("unknown command")
case _ is wildcard default fallback
Strings are immutable sequences of Unicode characters. Slicing, transforming, or replacing returns a brand new string.
s = "Python"
# s[start:stop:step] โ all three arguments are optional
s[0] # 'P' index 0 (first char)
s[-1] # 'n' last char (counts from end)
s[-2] # 'o' second to last
s[1:4] # 'yth' index 1 up to (not including) 4
s[:3] # 'Pyt' from start to index 3
s[2:] # 'thon' from index 2 to end
s[-3:] # 'hon' last 3 characters
s[::2] # 'Pto' every 2nd character (step=2)
s[1:5:2] # 'yh' start=1, stop=5, step=2
s[::-1] # 'nohtyP' step=-1 walks backwards โ reversed
Slicing works identically on list and tuple. Omitted args default to start=0, stop=len, step=1.
s.find("th") # starting index (or -1 if missing)
s.rfind("o") # search starting from the right
s.index("th") # starting index (raises ValueError if missing)
s.count("o") # count non-overlapping occurrences
"py" in s.lower() # membership test (case-insensitive)
s.startswith("Py") # True (also accepts tuple of prefixes)
s.endswith((".txt", ".md")) # check multiple extensions
find() returns -1 if missing; index() raises ValueError.
# Direct mutation is NOT allowed:
# s[0] = 'J' --> TypeError: 'str' object does not support item assignment
s.replace("old", "new", count=-1) # replace occurrences (optional count)
s.removeprefix("pre_") # remove prefix if present (3.9+)
s.removesuffix(".txt") # remove suffix if present (3.9+)
s.strip() # strip whitespace from both ends
s.lstrip(" _") / s.rstrip(" _") # strip custom characters
s.lower() / s.upper() / s.title() # case transforms
All string methods return a new string. Nothing mutates in place.
# "Append" (concatenation):
s = s + " extra" # concatenation (or s += "...")
# "Insert at index i":
i, insert_str = 2, "XYZ"
s = s[:i] + insert_str + s[i:]
# "Update last character":
s = s[:-1] + "Z"
# "Pop last character":
last_char, s = s[-1], s[:-1]
Slice notation s[:-1] isolates everything except the last character.
"a,b,c".split(",") # ['a', 'b', 'c']
"a,b,c".rsplit(",", maxsplit=1) # ['a,b', 'c'] (split from right)
"line1\nline2".splitlines() # ['line1', 'line2']
", ".join(["a", "b", "c"]) # "a, b, c" (join sequence with sep)
"42".zfill(5) # "00042" (zero-pad)
"hi".center(10, "-") # "----hi----"
join() is called on the separator string: sep.join(list).
f"Hello {name!r}" # !r=repr, !s=str, !a=ascii
f"Price: ${price:.2f}" # 2 decimal places: "$19.99"
f"Padded: {val:04d}" # zero-padded integer: "0007"
f"Percent: {pct:.1%}" # formatted percentage: "75.0%"
f"{x = }" # debug print (3.8+): "x = 42"
f-strings (Python 3.6+) evaluate expressions at runtime and are idiomatic.
Python provides expressive core collections: list (sequence), dict (key-value map), set (unique elements), tuple (immutable sequence), and stdlib extras: deque (double-ended queue), heapq (min-heap), namedtuple, Counter, defaultdict.
list) โ Mutable Sequence
lst: list[int] = [] # type-annotated empty list
lst = [1, 2, 3] # literal; lst = list() also works
lst.append(4) # add to end
lst.insert(1, 99) # insert at index
lst.extend([5, 6]) # append multiple items
val = lst.pop() # remove & return last
val = lst.pop(0) # remove & return at index
lst.remove(99) # remove first occurrence by value
lst[0] = 10 # update by index
lst[1:3] # slice โ returns new list
lst.sort() / lst.reverse() # in-place sort / reverse (return None)
evens = [x for x in lst if x % 2 == 0] # list comprehension
0-indexed dynamic arrays; preserve insertion order.
dict) โ Key-Value Map
d: dict[str, int] = {} # type-annotated empty dict
d = {"a": 1, "b": 2} # literal; dict(a=1, b=2) also works
d["c"] = 3 # insert or overwrite
d.get("x", 0) # safe lookup โ returns 0 if missing
d.pop("a") # remove key, return value
d.pop("a", None) # same but silent if key is missing
d.keys() / d.values() / d.items() # live views (not copies)
merged = d1 | d2 # merge โ right side wins on conflicts (3.9+)
last = next(reversed(d)) # last inserted key
{k: v for k, v in d.items() if v > 0} # dict comprehension
Hash map; insertion order preserved (3.7+).
set) โ Unique Unordered Elements
s: set[str] = set() # empty set โ {} creates a dict!
s = {"apple", "banana"} # set literal with values
s.add("cherry") # add element
s.discard("missing") # silent remove โ no error if absent
s.remove("apple") # remove โ raises KeyError if missing
a = {1, 2, 3}; b = {2, 3, 4}
a | b # {1,2,3,4} union (in a or b)
a & b # {2,3} intersection (in both)
a - b # {1} difference (in a but not b)
a ^ b # {1,4} symmetric difference (in a or b, not both)
{x*2 for x in s} # set comprehension
Hash-based; elements must be hashable (no lists or dicts).
tuple) โ Immutable Sequence
t: tuple[int, str] = (42, "hi") # fixed-size, heterogeneous
t: tuple[int, ...] = (1, 2, 3) # variable-length, homogeneous
single = (42,) # one-element โ trailing comma required
a, b, c = (1, 2, 3) # unpacking
first, *mid, last = (1, 2, 3, 4) # extended unpacking
t_new = t + (4,) # new tuple (immutable โ no in-place edit)
t[1:3] # slicing returns a new tuple
Fixed size and immutable; hashable when all contents are hashable โ usable as dict keys.
deque) โ Double-Ended Queue
from collections import deque
dq = deque([1, 2, 3]) # optional maxlen=N caps size (FIFO eviction)
dq.append(4) # add to right
dq.appendleft(0) # add to left
val = dq.pop() # remove & return from right
val = dq.popleft() # remove & return from left
dq.rotate(1) # shift right by 1 (negative = left)
list(dq) # [0, 1, 2, 3] โ convert to list
Use instead of list.pop(0) for front operations โ see table for complexity. Ideal for queues, BFS, and sliding windows.
heapq) โ Min-Heap / Priority Queue
import heapq
heap = []
heapq.heappush(heap, 3) # push
heapq.heappush(heap, 1)
val = heapq.heappop(heap) # pop min; returns 1
heap[0] # peek min without removing
heapq.heapify(lst) # convert list in-place
heapq.nlargest(2, lst) # top-2 largest
heapq.nsmallest(2, lst) # top-2 smallest
# max-heap: negate values
heapq.heappush(heap, -val)
max_val = -heapq.heappop(heap)
Python's heap is a min-heap. Negate values to simulate a max-heap.
Counter, defaultdict & namedtuple)
from collections import Counter, defaultdict, namedtuple
c = Counter("banana") # Counter({'a': 3, 'n': 2, 'b': 1})
c.most_common(2) # [('a', 3), ('n', 2)] โ top frequencies
dd = defaultdict(list) # auto-initializes [] for missing keys
dd["fruits"].append("apple") # safe append without prior key check
Point = namedtuple("Point", ["x", "y"]) # lightweight immutable record
p = Point(1, 2)
p.x / p.y # attribute access
x, y = p # unpacking like a regular tuple
p._replace(x=10) # returns a new instance with field changed
Counter tallies frequencies; defaultdict eliminates key existence checks; namedtuple gives tuple fields readable names.
| Operation | list |
deque |
heapq |
set |
dict |
tuple |
str |
|---|---|---|---|---|---|---|---|
| Access item | lst[i] (O(1)) | dq[i] (O(n) mid) | heap[0] (min, O(1)) | Unindexed (next(iter(s))) |
d[k] / d.get(k) (O(1)) | t[i] (O(1)) | s[i] (O(1)) |
| Access last item | lst[-1] (O(1)) | dq[-1] (O(1)) | N/A (heap order) | Unordered (list(s)[-1]) |
next(reversed(d)) (O(1)) | t[-1] (O(1)) | s[-1] (O(1)) |
| Add / Append | lst.append(x) (O(1)) | dq.append(x) (O(1)) | heappush(heap, x) (O(log n)) | s.add(x) (O(1)) | d[key] = val (O(1)) | t = t + (x,) (O(n)) | s = s + "x" (O(n)) |
| Add to front | lst.insert(0, x) (O(n)) | dq.appendleft(x) (O(1)) | N/A | N/A (unordered) | N/A (by key) | (x,) + t (O(n)) | "x" + s (O(n)) |
| Insert at index | lst.insert(i, x) (O(n)) | dq.insert(i, x) (O(n)) | N/A | N/A (unordered) | N/A (by key) | t[:i] + (x,) + t[i:] (O(n)) | s[:i] + "x" + s[i:] (O(n)) |
| Update item | lst[i] = val (O(1)) | dq[i] = val (O(n) mid) | N/A (pop + push) | s.discard(o); s.add(n) | d[k] = val (O(1)) | t[:i] + (v,) + t[i+1:] (O(n)) | s.replace(o, n, 1) (O(n)) |
| Remove by value | lst.remove(x) (O(n)) | dq.remove(x) (O(n)) | heap.remove(x); heapify (O(n)) | s.remove(x) / discard (O(1)) | del d[k] (O(1)) | tuple(x for x in t if x!=v) (O(n)) | s.replace(sub, "", 1) (O(n)) |
| Pop last | lst.pop() (O(1)) | dq.pop() (O(1)) | heappop(heap) (min, O(log n)) | s.pop() (arbitrary, O(1)) | d.pop(k) / d.popitem() (O(1)) | last, t = t[-1], t[:-1] (O(n)) | last, s = s[-1], s[:-1] (O(n)) |
| Pop first | lst.pop(0) (O(n)) | dq.popleft() (O(1)) | N/A | N/A | N/A | N/A | N/A |
| Check existence | x in lst (O(n)) | x in dq (O(n)) | x in heap (O(n)) | x in s (O(1)) | k in d (O(1)) | x in t (O(n)) | sub in s (O(n)) |
| Clear / Empty | lst.clear() | dq.clear() | heap.clear() | s.clear() | d.clear() | t = () | s = "" |
| Mutable in-place? | Yes | Yes | Yes (list) | Yes | Yes | No (Immutable) | No (Immutable) |
| Ordered? | Yes (0-indexed) | Yes (insertion) | Partial (heap order) | No (Unordered) | Yes (Insertion) | Yes (0-indexed) | Yes (0-indexed) |
Python supports single and multiple inheritance with duck typing. @dataclass removes boilerplate for data containers.
from dataclasses import dataclass, field
@dataclass
class Point:
x: float
y: float = 0.0
tags: list = field(default_factory=list)
# auto-generates __init__, __repr__, __eq__; frozen=True for immutability
__str__ # human-readable (used by print)
__repr__ # unambiguous (REPL/debug)
# __repr__ is fallback for both if only one is defined
@property
def name(self): return self._name
@name.setter
def name(self, v): self._name = v.strip()
@classmethod
def from_string(cls, s): # receives cls โ alternative constructor
return cls(*s.split(","))
@staticmethod
def validate(x): # no implicit arg โ namespaced utility
return x > 0
Catch specific exception types โ bare except: silences every error including KeyboardInterrupt.
try:
result = risky()
except (TypeError, ValueError) as e:
print(f"caught: {e}")
else:
print("no exception") # runs only if no exception raised
finally:
cleanup() # always runs
raise ValueError("message")
raise # bare raise re-raises, preserving traceback
with open("file.txt") as f:
data = f.read()
# calls __enter__ on entry, __exit__ on exit (even after exceptions)
Idiomatic Python favours readability and conciseness. Type hints improve editor support and catch bugs early without runtime enforcement.
if n := len(data):
print(f"got {n} items")
# assign and test in one expression
a, *rest, b = [1, 2, 3, 4, 5]
# a=1, rest=[2,3,4], b=5 โ works in function args too
fn = lambda x, y: x + y
# single-expression anonymous; prefer def for anything more complex
total = sum(x*x for x in range(100))
# lazy โ produces values one at a time, no list in memory
def greet(name: str) -> str:
return f"Hello {name}"
# Python 3.10+: use X | Y instead of Union[X, Y]
def parse(val: str | None) -> int | None:
return int(val) if val else None
# use mypy or pyright to enforce โ not checked at runtime