Ch 8 — Classes & Comprehensions
Ch 8 — Classes & Comprehensions
Aryan’s RAM manager now has dozens of functions — but they all share the same data and there’s no clean way to model a “process record” or a “snapshot”. Classes give that structure. Comprehensions collapse five-line loops into one expressive line, making the data-wrangling code that feeds the dashboard elegant and fast.
Classes
A class is a blueprint for objects. Each object (instance) bundles data (attributes) and behavior (methods).
class ProcessSnapshot:
"""Represents one RAM reading for a single process."""
def __init__(self, pid, name, rss_mb):
self.pid = pid
self.name = name
self.rss_mb = rss_mb
def rss_gb(self):
return self.rss_mb / 1024
def __repr__(self):
return f"ProcessSnapshot(pid={self.pid}, name={self.name!r}, rss_mb={self.rss_mb})"Creating instances
chrome = ProcessSnapshot(4821, "chrome", 1024.5)
print(chrome.name) # 'chrome'
print(chrome.rss_gb()) # 1.0009765625
print(chrome) # ProcessSnapshot(pid=4821, name='chrome', rss_mb=1024.5)__init__ and Instance Methods
__init__is the initializer — runs automatically when you callProcessSnapshot(...).selfis the first parameter of every instance method and refers to the calling object.- Dunder methods (
__repr__,__str__,__eq__) let you customize how Python handles your objects.
class ProcessSnapshot:
def __init__(self, pid, name, rss_mb):
self.pid = pid
self.name = name
self.rss_mb = rss_mb
def is_heavy(self, threshold_mb=512):
return self.rss_mb > threshold_mbInheritance
One class can extend another, inheriting all its attributes and methods.
class CriticalProcess(ProcessSnapshot):
"""A process flagged as a memory hog."""
def __init__(self, pid, name, rss_mb, reason):
super().__init__(pid, name, rss_mb)
self.reason = reason
def alert_msg(self):
return (
f"ALERT: {self.name} (PID {self.pid}) using "
f"{self.rss_mb:.1f} MB — {self.reason}"
)super().__init__(...) delegates to the parent class so you don’t duplicate code.
List Comprehensions
Build a new list by applying an expression to each element of an iterable.
# Old way
rss_values = []
for snap in snapshots:
rss_values.append(snap.rss_mb)
# Comprehension
rss_values = [snap.rss_mb for snap in snapshots]
# With filter
heavy = [snap for snap in snapshots if snap.rss_mb > 512]
# Transform + filter
heavy_names = [snap.name.upper() for snap in snapshots if snap.rss_mb > 512]Dict Comprehensions
# Build {pid: rss_mb} lookup
pid_rss = {snap.pid: snap.rss_mb for snap in snapshots}
# {4821: 1024.5, 312: 128.0, 9001: 512.0}
# Normalize process names
name_map = {snap.pid: snap.name.lower() for snap in snapshots}enumerate and zip
# Numbered table rows
for i, snap in enumerate(snapshots, start=1):
print(f"{i:>3}. {snap.name:<20} {snap.rss_mb:>8.1f} MB")
# Pair current vs previous snapshot
for prev, curr in zip(snapshots[:-1], snapshots[1:]):
delta = curr.rss_mb - prev.rss_mb
print(f"{curr.name}: Δ {delta:+.1f} MB")sorted and sort
# sorted — returns a new list, non-destructive
top5 = sorted(snapshots, key=lambda s: s.rss_mb, reverse=True)[:5]
# sort — in-place
snapshots.sort(key=lambda s: s.name)Use sorted() when you need the original order intact; use .sort() when in-place is fine.
map and filter
Functional alternatives to comprehensions.
# map — apply a function to every element
rss_list = list(map(lambda s: s.rss_mb, snapshots))
# filter — keep elements matching a predicate
heavy = list(filter(lambda s: s.rss_mb > 512, snapshots))Comprehensions are generally preferred for readability, but map/filter appear in existing codebases you will maintain.
Class Hierarchy
flowchart TD
A[ProcessSnapshot] --> B[__init__\npid name rss_mb]
A --> C[rss_gb method]
A --> D["__repr__"]
A --> E[is_heavy method]
F[CriticalProcess] -->|inherits| A
F --> G[reason attribute]
F --> H[alert_msg method]Comprehension Patterns
flowchart LR
A[Source iterable] --> B{Comprehension type}
B --> C["[expr for x in it]"]
B --> D["{k: v for x in it}"]
B --> E["{expr for x in it} — set"]
B --> F["(expr for x in it) — generator"]
C --> G[Add filter with 'if cond']
D --> G
E --> GKey Takeaways
- Classes bundle data (attributes) and behavior (methods) into reusable blueprints.
__init__is the initializer;selfrefers to the instance — always the first method parameter.- Inheritance via
super()lets child classes reuse and extend parent behavior. - Dunder methods (
__repr__,__str__,__eq__) integrate custom classes with Python’s protocols. - List comprehensions replace simple
for-appendloops with a single, readable expression. - Dict comprehensions build lookup tables in one line.
sorted()is non-destructive;.sort()is in-place — know which you need.map()andfilter()are functional alternatives; comprehensions are usually more Pythonic.