Python: Standard Library, 20 Must-Know Modules

The Python standard library ships with 200+ modules, and most developers only ever touch about 10 of them. Here are the 20 modules that save you from reinventing wheels, writing buggy workarounds, and reaching for pip when the answer is already sitting on your hard drive.

“Python comes with batteries included.”

Guido van Rossum, Python docs

Last Updated: July 2026 | Tested on: Python 3.14.6 | Difficulty: Intermediate | Reading Time: 20 minutes

The Python standard library is the set of modules that come bundled with Python itself. No pip install, no internet, no extra setup. “Batteries included” is the old motto, and knowing what is already in the box saves you from installing third-party packages for problems Python solved years ago.

Think of it like the toolbox that comes with a new house. You did not buy the wrench, the screwdriver, or the tape measure, but they are in the drawer when you need them. Most people forget they are there and go buy new ones. This post is the drawer tour: pathlib, collections, itertools, functools, json, re, datetime, and more. You will not memorize all 20 in one sitting. The goal is to build a mental index, so the next time you hit a problem your first thought is “does the standard library already do this?” Most of the time, it does.

The Cheat Sheet: 20 Must-Know Modules

PythonStandard LibraryFile and OSos OSinterfacepathlibPath objectsshutilFileoperationsglobPatternmatchingDataHandlingjson JSONencode/decodecsv CSVread/writesqlite3SQLitedatabasecollections ContainersText andRegexreRegularexpressionsstringStringconstantstextwrapTextformattingSystem andRuntimesysSystemconfigsubprocess RuncommandsargparseCLIargumentsloggingLog messagesDate andMathdatetimeDates/timesmathMathfunctionsrandomRandomnumbersitertoolsIteratortoolsPython Standard Library: Essential Modules Grouped by Category

Tip: click the diagram to open it full screen, then use the zoom and pan controls for a closer look.

The diagram groups the essential standard library modules by what they do: file and operating system (OS) work, data handling, text and regular expressions (regex), system and runtime, and date and math. Each group holds 3 to 4 modules you will reach for again and again on real projects, so treat this as a practical toolkit, not a full catalog. The reference table right below gives you a one-line job description for every module, so you can scan for the one you need and jump straight to its section.

ModuleWhat It DoesWhen You Need It
osOS interface for env vars and process infoReading environment variables, getting PID
pathlibObject-oriented file pathsAny file path manipulation (replaces os.path)
sysPython runtime configCommand-line args, exit codes, path manipulation
jsonJSON encode/decodeAPI responses, config files, data exchange
csvCSV read/writeSpreadsheet data, log files, data exports
datetimeDates, times, deltasTimestamps, scheduling, age calculation
reRegular expressionsPattern matching, text validation, parsing
collectionsSpecialized containersCounting, ordered dicts, default dicts, deques
itertoolsIterator building blocksCombinations, permutations, chaining, grouping
functoolsHigher-order function toolsCaching, partial functions, reduce
mathMath functionssqrt, ceil, floor, log, trig, factorial
randomRandom number generationShuffling, sampling, random choices
loggingLogging frameworkApplication logging (replace print debugging)
argparseCLI argument parserBuilding command-line tools
subprocessRun external commandsShell commands, piping, process management
shutilHigh-level file operationsCopy, move, delete directories, disk usage
sqlite3SQLite database interfaceLocal databases, prototyping, embedded data
unittestTesting frameworkUnit tests (though pytest is more popular)
typingType hint supportType annotations for functions, classes
dataclassesAuto-generated data classesStructured data without boilerplate

File & OS: pathlib, os, shutil

A file path stored as a raw string is like an address scribbled as one long line on a napkin: you can read it, but nothing on it is labelled. A Path object is a proper address card. It knows which part is the file name, which part is the extension, and which folder it lives in, and you can ask it questions like “do you actually exist on disk?”

📄 pathlib: the modern way to handle paths

from pathlib import Path

# Build paths with the / operator (works on Windows, Mac, Linux)
project = Path.home() / "projects" / "webapp"
config = project / "config" / "settings.json"

print(f"Home directory: {Path.home()}")
print(f"Config path: {config}")
print(f"File name: {config.name}")     # settings.json
print(f"Stem: {config.stem}")          # settings
print(f"Extension: {config.suffix}")   # .json
print(f"Parent: {config.parent}")      # .../config
print(f"Exists? {config.exists()}")    # False, we never created it

# List every .py file in the current folder
py_files = list(Path(".").glob("*.py"))
print(f"Python files here: {len(py_files)}")

▶ Output (Windows, your home path and file count will differ)

Home directory: C:\Users\Rahul
Config path: C:\Users\Rahul\projects\webapp\config\settings.json
File name: settings.json
Stem: settings
Extension: .json
Parent: C:\Users\Rahul\projects\webapp\config
Exists? False
Python files here: 11

What happened here: Notice we never glued strings together with slashes by hand. The / operator builds the path for you, and Python prints it with the right separator for your operating system: backslashes on Windows, forward slashes on Mac and Linux. The same code runs everywhere without a single if platform == check. The home directory and the file count come from the machine this ran on, so yours will look different. Everything else (.name, .stem, .suffix, .parent) is pure path logic and will match exactly.

📄 shutil: heavy-duty file operations

import shutil

# How much room is left on the drive?
total, used, free = shutil.disk_usage("/")
print(f"Total: {total // (1024**3)} GB")
print(f"Used:  {used // (1024**3)} GB")
print(f"Free:  {free // (1024**3)} GB")

# These are commented out so the demo does not touch your files:
# shutil.copy("source.txt", "backup.txt")     # Copy a file
# shutil.copytree("src_dir", "backup_dir")    # Copy a whole folder tree
# shutil.move("old_loc/file.txt", "new_loc/") # Move a file
# shutil.rmtree("temp_dir")                    # Delete a folder tree (no undo)

▶ Output (numbers depend on your disk)

Total: 476 GB
Used:  388 GB
Free:  87 GB

What happened here: shutil is the module for the file jobs that feel like chores: copying, moving, and deleting whole folders. Writing that yourself with a loop is fiddly and easy to get wrong. The // (1024**3) bit just turns raw bytes into gigabytes (1024 bytes make a kilobyte, and so on three times up). One warning worth tattooing somewhere: shutil.rmtree deletes a folder and everything inside it with no trip to the recycle bin. There is no undo.

Data Handling: json, csv, collections

The examples from here on reuse one running cast: a small dev team where Rahul is the lead, Niranjan works on the backend, Viraj handles the frontend, and teammates Anvi, Anvay, Aviraj, and Aditi show up as we go. First job for this team: turning their data into JSON and back.

📄 json: the universal data exchange format

import json

team = {
    "project": "TechnoScripts",
    "members": [
        {"name": "Rahul", "role": "lead", "age": 28},
        {"name": "Niranjan", "role": "backend", "age": 26},
        {"name": "Viraj", "role": "frontend", "age": 25}
    ],
    "active": True
}

# Python dict to JSON text (dumps = "dump string")
json_str = json.dumps(team, indent=2)
print(json_str[:120] + "...")

# JSON text back to a Python dict (loads = "load string")
parsed = json.loads(json_str)
print(f"\nProject: {parsed['project']}")
print(f"Lead: {parsed['members'][0]['name']}")

▶ Output

{
  "project": "TechnoScripts",
  "members": [
    {
      "name": "Rahul",
      "role": "lead",
      "age": 28
    },...

Project: TechnoScripts
Lead: Rahul

What happened here: json is the translator between Python and the rest of the world. dumps turns a dict into a text string you can save to a file or send over the network, and loads turns that text back into a dict. The indent=2 makes the output human-readable with two-space nesting. One detail beginners miss: Python’s True becomes lowercase true in JSON, and None becomes null. The [:120] slice just chops the string at 120 characters so the demo does not flood your screen, which is why the output cuts off partway through, right after the first member.

📄 collections: Counter, defaultdict, deque

from collections import Counter, defaultdict, deque

# Counter: tally up anything hashable in one line
languages = ["Python", "Java", "Python", "Go", "Python", "Java", "Rust"]
counts = Counter(languages)
print(f"Language counts: {counts}")
print(f"Top 2: {counts.most_common(2)}")

# defaultdict: a dict that hands you a fresh empty list instead of a KeyError
team_projects = defaultdict(list)
team_projects["Anvi"].append("API Gateway")
team_projects["Anvi"].append("Auth Service")
team_projects["Aviraj"].append("Dashboard")
print(f"\nAnvi's projects: {team_projects['Anvi']}")
print(f"Unknown member: {team_projects['nobody']}")  # Returns [], not a KeyError

# deque: a list with a memory limit, drops the oldest when full
recent_searches = deque(maxlen=3)
recent_searches.append("python itertools")
recent_searches.append("flask tutorial")
recent_searches.append("docker compose")
recent_searches.append("kubernetes pods")  # The oldest item falls off the back
print(f"\nRecent searches: {list(recent_searches)}")

▶ Output

Language counts: Counter({'Python': 3, 'Java': 2, 'Go': 1, 'Rust': 1})
Top 2: [('Python', 3), ('Java', 2)]

Anvi's projects: ['API Gateway', 'Auth Service']
Unknown member: []

Recent searches: ['flask tutorial', 'docker compose', 'kubernetes pods']

What happened here: These three are the upgrades you wish the plain dict and list shipped with. Counter is like a tally sheet at the door of a party, every time a guest walks in you add a mark next to their name, and most_common(2) tells you the two most frequent in seconds. defaultdict(list) fixes the most annoying part of grouping data, here each teammate (Anvi, Aviraj) gets their own project list: with a normal dict, d["nobody"] blows up with a KeyError, but here it quietly hands back an empty list so you can append without checking first.

And deque with maxlen=3 behaves like a three-slot shelf: push a fourth book on and the oldest one falls off the other end, which is exactly how a “recent searches” list should work.

Iteration: itertools, functools

📄 itertools: lazy iteration utilities

from itertools import chain, combinations, groupby, islice

# chain: stitch several iterables into one stream
frontend = ["HTML", "CSS", "JS"]
backend = ["Python", "Go", "Rust"]
all_skills = list(chain(frontend, backend))
print(f"All skills: {all_skills}")

# combinations: every unique pair, no repeats, order ignored
team = ["Rahul", "Anvay", "Aditi"]
pairs = list(combinations(team, 2))
print(f"Review pairs: {pairs}")

# islice: take a slice of any iterator, even an endless one
squares = (x**2 for x in range(1000))   # x starts at 0, so 0**2 = 0
first_five = list(islice(squares, 5))
print(f"First 5 squares: {first_five}")

# groupby: group neighbours that share a key (sort by that key first!)
scores = [("A", 95), ("A", 88), ("B", 72), ("B", 68), ("A", 91)]
scores.sort(key=lambda x: x[0])  # Without this, the two A groups stay split
for grade, group in groupby(scores, key=lambda x: x[0]):
    print(f"Grade {grade}: {[s[1] for s in group]}")

▶ Output

All skills: ['HTML', 'CSS', 'JS', 'Python', 'Go', 'Rust']
Review pairs: [('Rahul', 'Anvay'), ('Rahul', 'Aditi'), ('Anvay', 'Aditi')]
First 5 squares: [0, 1, 4, 9, 16]
Grade A: [95, 88, 91]
Grade B: [72, 68]

What happened here: itertools is a box of patient iterators. They do not build giant lists in memory, they hand you one item at a time, which is how islice can safely take 5 values out of a generator that could produce a thousand. Watch that squares line: range(1000) starts at 0, so the very first square is 0**2 = 0, and the first five come out as [0, 1, 4, 9, 16], not [1, 4, 9, 16, 25].

That off-by-one catches people constantly. The other catch lives in groupby: it only groups items that sit next to each other, so you must sort by the same key first. Skip the sort and you get two separate “A” groups instead of one.

📄 functools: lru_cache and partial

from functools import lru_cache, partial
import time

# lru_cache: remember past results so repeats are free
@lru_cache(maxsize=128)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

start = time.perf_counter()
result = fibonacci(100)
elapsed = time.perf_counter() - start
print(f"fib(100) = {result}")
print(f"Time: {elapsed:.6f}s (cached, so it barely registers)")

# partial: lock in some arguments now, fill the rest later
def greet(greeting, name):
    return f"{greeting}, {name}!"

hello = partial(greet, "Hello")
namaste = partial(greet, "Namaste")
print(hello("Aditi"))
print(namaste("Anvay"))

▶ Output

fib(100) = 354224848179261915075
Time: 0.000227s (cached, so it barely registers)
Hello, Aditi!
Namaste, Anvay!

What happened here: @lru_cache is a sticky note for function results. The first time fibonacci(100) runs it does the work, then it pins the answer to the wall. Ask for it again and it reads the note instead of recomputing. Without the cache, fibonacci(100) would branch into astronomically many repeated calls and effectively never finish. With it, the whole thing lands in a fraction of a millisecond. Your exact time will differ run to run, that is fine, the point is it is tiny.

partial is the other handy tool: it takes a function and freezes one argument, so hello = partial(greet, "Hello") gives you a new function that only needs a name. Same idea as a coffee order saved as “the usual”.

System: sys, subprocess, argparse

Quick way to keep these two straight: sys is your program looking in the mirror (what version am I running on, what arguments was I started with), while subprocess is your program picking up the phone to ask another program to do a job and report back.

📄 sys: runtime info and control

import sys

print(f"Python version: {sys.version}")
print(f"Platform: {sys.platform}")
print(f"Max int size: {sys.maxsize}")
print(f"Recursion limit: {sys.getrecursionlimit()}")
print(f"Script arguments: {sys.argv}")

# sys.exit(0)   # Quit with a success code
# sys.exit(1)   # Quit with an error code

▶ Output

Python version: 3.14.6 (tags/v3.14.6:c63aec6, Jun 10 2026, 10:26:10) [MSC v.1944 64 bit (AMD64)]
Platform: win32
Max int size: 9223372036854775807
Recursion limit: 1000
Script arguments: ['7_sys.py']

What happened here: Where os talks to your computer, sys talks to the Python interpreter running your code. The version string is exactly what the interpreter reports, so it changes with your Python build (this ran on Python 3.14.6 on Windows, hence win32 and the MSC compiler tag). sys.argv is the list of words you typed after python on the command line, which is how scripts read their own arguments. And sys.maxsize is not the biggest integer Python can hold (Python ints are unbounded), it is the largest index a list can have on this machine.

📄 subprocess: run other programs from Python

import subprocess
import sys

# Run a command and capture what it prints
result = subprocess.run(
    [sys.executable, "--version"],   # sys.executable = the python running this
    capture_output=True,
    text=True                        # text=True gives strings, not raw bytes
)
print(f"stdout: {result.stdout.strip()}")
print(f"Return code: {result.returncode}")  # 0 means success

# Pass arguments as a list, no shell needed (safer, cross-platform)
result = subprocess.run(
    [sys.executable, "-c", "print('Hello from a subprocess')"],
    capture_output=True,
    text=True
)
print(f"Captured: {result.stdout.strip()}")

▶ Output

stdout: Python 3.14.6
Return code: 0
Captured: Hello from a subprocess

What happened here: subprocess.run launches another program, waits for it to finish, and hands you back what it printed plus its exit code. A return code of 0 means “all good”, anything else means trouble. Notice we pass the command as a list of separate arguments rather than one big string. That is the safe habit: it sidesteps shell=True, which can let user input sneak in extra commands (a real security hole) and which also quotes things differently on Windows versus Mac and Linux. Using sys.executable instead of the literal "python" guarantees you call the exact same interpreter that is running your script.

The third module in this group, argparse, deserves a quick word even without a demo. Reading sys.argv by hand works for one or two arguments, but the moment your script grows flags like --verbose or --output file.txt, switch to argparse: you declare each argument once and it handles parsing, type conversion, error messages, and a free -h help screen.

Date, Time & Math: datetime, random, math

Doing date math by hand is like counting change in a currency where every note has a different value: 30 days here, 31 there, 28 or 29 in February. It is exactly the kind of arithmetic humans fumble and computers should own, and datetime owns it.

📄 datetime: dates, times, and durations

from datetime import datetime, timedelta, date

now = datetime.now()
print(f"Now: {now.strftime('%Y-%m-%d %H:%M')}")

birthday = date(1998, 5, 15)
age = (date.today() - birthday).days // 365   # rough age in whole years
print(f"Aviraj's age: {age}")

# timedelta is "an amount of time" you can add to a date
deadline = now + timedelta(days=14, hours=8)
print(f"Deadline: {deadline.strftime('%B %d, %Y at %I:%M %p')}")

# strptime parses a string into a real date object
release = datetime.strptime("2026-03-15", "%Y-%m-%d")
print(f"Release date: {release.date()}")

▶ Output (run on 2026-06-21, your “Now” and deadline will differ)

Now: 2026-06-21 15:26
Aviraj's age: 28
Deadline: July 05, 2026 at 11:26 PM
Release date: 2026-03-15

What happened here: Date math is the kind of thing people get painfully wrong by hand (leap years, month lengths, the 31st that does not exist). datetime handles all of it. Subtract one date from another and you get a timedelta, an “amount of time” with a .days count. Add a timedelta to a date and it rolls the calendar forward correctly, which is how the deadline jumps cleanly from June into July. strftime formats a date into text (the %Y-%m-%d style codes), and strptime does the reverse, reading text back into a real date.

The first two lines depend on the clock, so they will read differently when you run it. The age divides total days by 365, so it is close but not birthday-exact.

📄 random: random selection and shuffling

import random

random.seed(42)  # Same seed = same "random" results every run

team = ["Rahul", "Niranjan", "Viraj", "Anvi", "Anvay", "Aditi"]
print(f"Random reviewer: {random.choice(team)}")   # pick one
print(f"Two reviewers: {random.sample(team, 2)}")  # pick two, no repeats

random.shuffle(team)  # shuffles the list in place
print(f"Shuffled order: {team}")

print(f"Random int 1-100: {random.randint(1, 100)}")
print(f"Random float 0-1: {random.random():.4f}")

▶ Output

Random reviewer: Aditi
Two reviewers: ['Rahul', 'Aditi']
Shuffled order: ['Anvi', 'Anvay', 'Rahul', 'Niranjan', 'Viraj', 'Aditi']
Random int 1-100: 95
Random float 0-1: 0.1025

What happened here: The trick that surprises people is random.seed(42). “Random” numbers from a computer are not truly random, they come from a formula that starts at a seed. Plant the same seed and you get the exact same sequence every single time, which is why the output above is identical on every run and on your machine too. That is gold for testing: you want a shuffle you can reproduce. Remove the seed line and the results change on every run. Note random.sample picks without repeats (good for “pick 2 different reviewers”), while random.choice can pick the same item twice across calls.

Rounding out this group, the math module covers the number work that plain operators cannot: math.sqrt, math.floor and math.ceil, math.factorial, math.log, and the trig functions. If you catch yourself writing x ** 0.5 or hand-rolling a rounding trick, math already has a tested, faster version.

The “Python Way” Callouts

Two pieces of code can both work and still feel worlds apart. Experienced Python developers reach for the idiomatic option almost on reflex. Here are two habits worth copying early.

💡 Use pathlib instead of os.path

import os
from pathlib import Path

# Old way: os.path, gluing strings together
full_path = os.path.join(os.path.expanduser("~"), "projects", "app.py")

# New way: pathlib, real path objects and the / operator
full_path = Path.home() / "projects" / "app.py"

# pathlib is the modern standard. Use it for all new code.

💡 Use logging instead of print for debugging

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Instead of: print(f"Processing user {user_id}")
logger.info("Processing user %s", "Rahul")
logger.warning("Rate limit approaching: %d/100", 85)
logger.error("Failed to connect to database")

# Logging gives you levels, formatting, file output, and rotation. print gives you none of that.

▶ Output

INFO:__main__:Processing user Rahul
WARNING:__main__:Rate limit approaching: 85/100
ERROR:__main__:Failed to connect to database

What happened here: Every message comes out tagged with its level (INFO, WARNING, ERROR) and where it came from. That label is the whole point. When something breaks in production at 2am, you can show only the errors and ignore the chatter, something a wall of print statements can never do. Later you can send these same messages to a file, rotate them daily, or ship them to a monitoring tool without touching a single logger.info line. Note the %s and %d style: logging fills those in only if the message actually gets shown, which saves a little work on messages you filter out.

Common Mistakes

❌ Mistake: Installing what’s already built in

# No need to pip install a JSON library, json is built in
# No need to pip install a CSV library, csv is built in
# No need to write your own LRU cache, functools.lru_cache exists
# No need to pip install a path library, pathlib has shipped since 3.4
# Check the standard library FIRST, before you reach for pip

❌ Mistake: Using os.path when pathlib exists

# os.path still works, but pathlib is cleaner, safer, and cross-platform
# os.path.join() returns plain strings, which are easy to mangle by accident
# Path objects validate paths, support the / operator, and carry helpful methods

Conclusion

The Python standard library is huge, and you just toured the corner of it that earns its keep every day. Pathlib, json, csv, collections, itertools, functools, datetime, random, sys, argparse: these alone cover file handling, data wrangling, iteration, system work, and command-line tools. You will not remember every method, and you do not need to. Remember the names, remember roughly what each one is for, and let “batteries included” do the rest. The next time you are about to pip install something or hand-roll a workaround, pause and check the toolbox first. The answer is often already there.

You have leaned on modules, packages, and libraries all the way through this series, sometimes without stopping to name them. Next, in the module vs package tutorial, you finally get the clean answer to the question every beginner trips over: what exactly is the difference between a module, a package, a library, and a framework? And if you want the full map of everything covered so far, browse the Python + AI/ML tutorial series home.

Practice Exercises

  1. Exercise 1: List all files with sizes using os and pathlib.
  2. Exercise 2: Calculate days until New Year with datetime.
  3. Exercise 3: Convert between JSON and CSV using json, csv, pathlib.

Frequently Asked Questions

What is the Python standard library?

The Python standard library is a collection of 200+ modules that ship with every Python installation. You do not need to pip install them, they are already available. It includes tools for file I/O, data handling, math, dates, regular expressions, and much more.

What is the most useful Python standard library module?

It depends on your work, but pathlib (file paths), json (data exchange), collections (specialized containers), and itertools (iteration tools) are useful almost everywhere. For web developers, add logging and subprocess. For data work, add csv and datetime.

Should I use pathlib or os.path?

Use pathlib for all new code. It is object-oriented, cross-platform, and more readable than os.path string manipulation. os.path still works and you will see it in older codebases, but pathlib has been the recommended approach since Python 3.4.

What is functools.lru_cache?

@lru_cache is a decorator that automatically caches function results. When you call the function with the same arguments again, it returns the cached result instead of recomputing. LRU stands for ‘Least Recently Used’, so it evicts the oldest entries when the cache fills up.

How do I find all modules in the standard library?

Run help('modules') in the Python REPL to list every available module. Or check the official documentation at docs.python.org/3/library/. The dir(module) function lists everything inside one specific module.

What is the difference between os and sys modules?

os talks to the operating system: file operations, environment variables, process management. sys talks to the Python interpreter itself: command-line arguments, the module search path, stdin and stdout, recursion limits. A quick way to remember it: os is your computer, sys is your Python runtime.

Interview Questions on the Python Standard Library

These come from real screens and onsites. Practice answering before you read each answer.

Q: Your script builds a command with subprocess.run(f”convert {filename}”, shell=True) and a security review flags it. What is the risk and the fix?

The risk is shell injection. If filename comes from a user and contains something like photo.png; rm -rf ~, the shell happily runs the extra command. The fix is to drop shell=True and pass the command as a list of separate arguments: subprocess.run(["convert", filename]). Each list item is delivered to the program as one argument, so nothing in the filename can be interpreted as a new command.

Q: A long-running Application Programming Interface (API) service decorates a lookup function with @lru_cache(maxsize=None), takes user IDs as arguments, and memory keeps climbing. What do you check first?

Check the cache size first: maxsize=None means unbounded, so every distinct user ID ever seen stays in memory forever. Call lookup.cache_info() to see how many entries have piled up. The fix is a bounded cache like @lru_cache(maxsize=1024) so old entries get evicted. Also watch for lru_cache on instance methods: the cache holds a reference to self, which keeps whole objects alive.

Q: Why can itertools.groupby return two separate groups for the same key, and how do you prevent it?

groupby only groups items that sit next to each other in the input, it never scans the whole sequence. So if the data is unsorted, the same key can appear in several runs and you get several groups for it. Sort the data by the exact same key function before calling groupby, and each key collapses into one group.

Q: When would you pick collections.deque over a plain list?

Pick deque when you add or remove items at both ends. deque.popleft() and appendleft() are O(1), while list.pop(0) is O(n) because every remaining element has to shift over. That makes deque the right tool for queues and sliding windows, and maxlen gives you a free ring buffer, like the recent-searches example in this post. For random access by index, a list is still better.

Q: Is the random module suitable for generating password reset tokens?

No. random uses the Mersenne Twister algorithm, which is deterministic: an attacker who observes enough outputs can predict the next ones. For anything security-related, use the secrets module, also in the standard library, for example secrets.token_urlsafe(32). Keep random for simulations, sampling, and shuffling where reproducibility is a feature, not a bug.

Q: Your app stores timestamps with datetime.now() and users in other time zones see wrong times. What went wrong and how do you fix it?

datetime.now() with no argument returns a naive datetime: it has no time zone attached, so it silently means “whatever zone the server is in”. Store aware datetimes instead: datetime.now(timezone.utc) from the datetime module, then convert to each user’s zone at display time with zoneinfo.ZoneInfo, for example .astimezone(ZoneInfo("Asia/Kolkata")). The rule of thumb is store in UTC, convert at the edges.

Further reading: the official Python documentation is the authoritative source on this.

Previous: Python: Packages, __init__.py, Relative Imports

Next: Python: Module vs Package vs Library vs Framework, What’s the Difference?

Series Home: Python + AI/ML Tutorial Series

RahulAuthor posts

Avatar for Rahul

Rahul is a passionate IT professional who loves to sharing his knowledge with others and inspiring them to expand their technical knowledge. Rahul's current objective is to write informative and easy-to-understand articles to help people avoid day-to-day technical issues altogether. Follow Rahul's blog to stay informed on the latest trends in IT and gain insights into how to tackle complex technical issues. Whether you're a beginner or an expert in the field, Rahul's articles are sure to leave you feeling inspired and informed.

No comment

Leave a Reply

Your email address will not be published. Required fields are marked *