← Back to Course Index
Lesson 6 of 10

The Standard Library

~20 min · os, sys, datetime, collections, itertools, re, subprocess

Ref
Primary Source
Python Standard Library Reference — docs.python.org

"Batteries included." Python's stdlib is enormous. This lesson maps the most useful modules to their JS equivalents so you know what to reach for.

1 — os & sys: System Interface

import os
import sys

# Environment variables (like process.env in Node)
db_url = os.environ.get("DATABASE_URL", "sqlite:///dev.db")
os.environ["MY_VAR"] = "value"

# Working directory (like process.cwd())
cwd = os.getcwd()
os.chdir("/tmp")

# File system operations
os.makedirs("a/b/c", exist_ok=True)   # mkdir -p
os.remove("file.txt")
os.rename("old.txt", "new.txt")
os.listdir(".")                        # list directory

# sys — Python runtime
sys.argv        # command-line args (like process.argv)
sys.exit(0)     # exit with code
sys.path        # module search paths (like NODE_PATH)
sys.platform    # "darwin", "linux", "win32"
sys.version     # Python version string

# Run a shell command (prefer subprocess)
exit_code = os.system("ls -la")   # avoid — no output capture

2 — subprocess: Run External Commands

The Node.js child_process equivalent. Use subprocess.run() for most things.

import subprocess

# Run a command and capture output
result = subprocess.run(
    ["git", "log", "--oneline", "-10"],
    capture_output=True,    # capture stdout and stderr
    text=True,              # decode bytes → str
    check=True              # raise CalledProcessError if exit code != 0
)
print(result.stdout)
print(result.returncode)   # 0

# Shell string (avoid for user input — security risk!)
result = subprocess.run("echo hello", shell=True, text=True, capture_output=True)

# Get output as string quickly
output = subprocess.check_output(["ls", "-la"], text=True)

3 — datetime: Dates & Times

from datetime import datetime, date, timedelta
import zoneinfo  # Python 3.9+

# Current time (naive — no timezone)
now = datetime.now()
today = date.today()

# Create a datetime
dt = datetime(2026, 6, 23, 22, 30, 0)

# Format → string (like .toISOString() or .toLocaleDateString())
dt.strftime("%Y-%m-%d %H:%M:%S")   # "2026-06-23 22:30:00"
dt.isoformat()                      # "2026-06-23T22:30:00"

# Parse string → datetime
dt = datetime.strptime("2026-06-23", "%Y-%m-%d")

# Arithmetic with timedelta
tomorrow = today + timedelta(days=1)
in_2_weeks = today + timedelta(weeks=2)
diff = datetime(2026,12,31) - datetime.now()
print(diff.days)  # days until new year

# Timezone-aware (Python 3.9+)
tz = zoneinfo.ZoneInfo("Asia/Kolkata")
now_ist = datetime.now(tz)

4 — collections: Supercharged Data Structures

from collections import defaultdict, Counter, OrderedDict, deque, namedtuple

# defaultdict — never worry about missing keys
word_count = defaultdict(int)
for word in text.split():
    word_count[word] += 1   # no KeyError if word is new

freq = defaultdict(list)
freq["fruits"].append("apple")  # no init needed

# Counter — count hashable items
from collections import Counter
votes = Counter(["Alice","Bob","Alice","Alice","Bob"])
votes.most_common(2)   # [("Alice", 3), ("Bob", 2)]
votes["Alice"]         # 3
votes["Charlie"]       # 0 (not KeyError!)

# deque — O(1) append/pop from both ends (double-ended queue)
q = deque([1, 2, 3])
q.appendleft(0)        # O(1) — list.insert(0,x) is O(n)
q.popleft()            # O(1) — perfect for queues/BFS
q.rotate(1)            # rotate right

# namedtuple — lightweight immutable record
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
p.x   # 3
p[0]  # 3 — still indexable

# OrderedDict — dict with insertion-order guarantees (mostly redundant in Python 3.7+ where dicts maintain order)
od = OrderedDict([("a", 1), ("b", 2)])

5 — itertools: Composable Iterators

Functional programming tools — faster than writing loops, and memory-efficient (lazy).

import itertools

nums = [1, 2, 3]

# chain — flatten iterables (like Array.flat or concat)
list(itertools.chain([1,2], [3,4], [5]))   # [1,2,3,4,5]

# product — cartesian product (nested loops)
list(itertools.product("AB", [1,2]))
# [("A",1), ("A",2), ("B",1), ("B",2)]

# combinations & permutations
list(itertools.combinations("ABC", 2))   # [("A","B"),("A","C"),("B","C")]
list(itertools.permutations("AB"))       # [("A","B"),("B","A")]

# groupby — group consecutive elements (MUST be sorted first)
from itertools import groupby
data = sorted([("a",1),("b",2),("a",3)], key=lambda x: x[0])
for key, group in groupby(data, key=lambda x: x[0]):
    print(key, list(group))

# islice — lazy slice of any iterator
from itertools import islice
first_5 = list(islice(some_generator, 5))

# count, cycle, repeat — infinite iterators
for i in itertools.islice(itertools.count(10, 2), 5):
    print(i)   # 10 12 14 16 18

6 — re: Regular Expressions

JavaScript
const text = "hello 42 world 99";

// Test (boolean)
/\d+/.test(text);             // true

// Match first
text.match(/\d+/);            // ["42"]

// Match all
text.match(/\d+/g);           // ["42", "99"]

// Replace
text.replace(/\d+/g, "NUM"); // "hello NUM world NUM"

// Named groups
const m = "2026-06-23".match(
  /(?\d{4})-(?\d{2})-(?\d{2})/
);
m.groups.y;  // "2026"
Python
import re
text = "hello 42 world 99"

# Test (boolean)
bool(re.search(r"\d+", text))   # True

# Match first — returns Match object or None
m = re.search(r"\d+", text)
m.group()     # "42"

# Find all
re.findall(r"\d+", text)        # ["42", "99"]

# Replace
re.sub(r"\d+", "NUM", text)     # "hello NUM world NUM"

# Named groups
m = re.search(
    r"(?P\d{4})-(?P\d{2})-(?P\d{2})",
    "2026-06-23"
)
m.group("y")    # "2026"

# Compile for reuse
pattern = re.compile(r"\d+")
pattern.findall(text)           # ["42", "99"]
💡 Always use raw strings for regex

Use r"\d+" not "\d+". Raw strings prevent Python from interpreting \d as an escape sequence before the regex engine sees it.

7 — functools: Higher-Order Functions

from functools import reduce, partial, lru_cache, wraps

# reduce — like Array.reduce()
from functools import reduce
total = reduce(lambda acc, x: acc + x, [1,2,3,4], 0)  # 10

# partial — pre-fill arguments (like .bind() in JS)
def power(base, exp):
    return base ** exp

square = partial(power, exp=2)
cube   = partial(power, exp=3)
square(4)   # 16
cube(3)     # 27

# lru_cache — memoize a function (like a manual cache in JS)
@lru_cache(maxsize=None)
def fibonacci(n):
    if n < 2: return n
    return fibonacci(n-1) + fibonacci(n-2)

fibonacci(100)   # instant, no stack overflow

# wraps — preserves function metadata when writing decorators
from functools import wraps
def my_decorator(func):
    @wraps(func)   # copies __name__, __doc__, etc. to wrapper
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

🧠 Quiz

1. What stdlib module is the equivalent of Node's process.env?

2. Counter(["a","b","a"])["z"] returns:

3. What does @lru_cache do?

4. In regex, why use r"\d+" instead of "\d+"?

0/4

Questions answered correctly.