Updated for Python 3.12+ · June 2026

Learn Python 2026
A free, structured curriculum

A full-spectrum Python education from absolute beginner to real-world automation. 950+ runnable examples, 18 topic modules, written to be read start to finish or used as a reference.

0
Runnable examples
0
Topic modules
0
Years of notes behind it
hello_python.py
def greet(name: str) -> str:
    return f"Hello, {name}! Python is 🐍"

learners = ["Ana", "Carlos", "Samuel"]
for learner in learners:
    print(greet(learner))
$ python hello_python.py
Hello, Ana! Python is 🐍
Hello, Carlos! Python is 🐍
Hello, Samuel! Python is 🐍

📖 Welcome to the Complete Python Learning Journey

This is a structured, free, and interactive curriculum designed to take you from absolute beginner to a confident Python developer — organised the way I wish a single resource had been organised when I was learning.

📜 The Python 2026 Curriculum — What You Will Learn

This curriculum is sequenced from first principles to production‑grade patterns, covering not just syntax but the why behind every feature — the data model, the GIL, memory management, the type system, and the design philosophy that makes Python readable by design rather than by accident.

You will write and run real code from lesson one, using Pyodide in your browser — no setup required. Every module is paired with runnable examples, step‑by‑step explanations, and analogies chosen to make abstract topics concrete. You'll build a small portfolio along the way: CLI tools, web scrapers, data visualisations, REST APIs, and AI‑powered applications.

By the end, the goal is that you think in Python — not just write it: understanding how to design robust systems, debug with confidence, and read other people's code without getting lost. This is meant as a solid foundation for data science, backend engineering, DevOps, or any field where Python shows up.

18 topic modules 950+ runnable examples roughly 8–10 weeks to cover the foundations

🚀 What This Curriculum Covers

Python is used across an unusually wide range of work — AI research, office automation scripts, machine-learning pipelines, backend services. This curriculum walks through the language systematically so you're not left guessing which parts matter.

We cover 18 topic modules, sequenced from fundamentals to advanced:

  • 🐍 Python Basics – Variables, data types, operators, control flow, loops, and functions — the foundation of everything.
  • 📦 Data Structures – Strings, lists, dictionaries, tuples, and sets — the tools for organising data.
  • 🏛️ Object‑Oriented Programming – Classes, inheritance, polymorphism, encapsulation, and the dunder methods that make Python objects behave predictably.
  • ⚡ Advanced Python – Generators, decorators, context managers, async/await, type hints, and static analysis with mypy.
  • 📁 File I/O & Modules – Read/write files, use pathlib, structure projects with packages, and handle exceptions properly.
  • 🚀 Applied Domains – Web scraping, data analysis (pandas, NumPy, Matplotlib), AI (LLM APIs, NLP), machine learning (scikit‑learn, TensorFlow), and more.

Every concept is accompanied by runnable code you can execute directly in your browser using Pyodide, without installing anything. Step‑by‑step explanations break down each block so you're not left studying syntax in isolation.

🤔 Why This Curriculum Is Organised This Way

Python's Reach

Python shows up across nearly every domain of modern software — web backends, data pipelines, ML deployment, and everyday automation. Understanding it well opens doors across all of these.

Where Beginners Get Stuck

Not understanding Python's quirks tends to cause specific, avoidable problems:

  • Floating‑point rounding errors in numeric code
  • Memory‑hungry code that slows down or crashes on larger inputs
  • Concurrency bugs that are hard to reproduce
  • Bugs from mutable default arguments or unclear type handling

This curriculum tries to explain the why behind each feature, so these problems are recognisable before they cause real trouble.

The Standard Library as a Starting Point

Python's "batteries included" philosophy gives you a genuinely useful standard library: math, random, datetime, collections, itertools, asyncio, and dozens more — all available without installing anything. Combined with the third‑party ecosystem on PyPI, this is often enough to build real tools without much external dependency.

Theory Alongside Practice

Beyond syntax, this curriculum explains why Python behaves the way it does — the data model, the GIL, memory management, the type system. That understanding is what lets you write idiomatic code and debug issues that would otherwise be mysterious.

Where Python Is Actually Used

  • Data Science & AI – one of the most common languages in the field
  • Backend Development – Flask, Django, and FastAPI back a large share of web services
  • DevOps & Automation – Python scripts frequently glue infrastructure together
  • Scientific Computing – widely used for simulations and analysis in research settings
  • General Software Engineering – a solid, readable choice for many kinds of projects

🌟 On Numbers, Data, and Abstraction

Numbers as a Foundation

Every digital system ultimately relies on numbers. When you write code, you're manipulating symbols that represent numbers underneath. Understanding how these numbers are stored, manipulated, and transformed is understanding a basic layer of how software works.

Building Blocks of Abstraction

Numbers are the foundation other abstractions are built on. Strings are numbers in disguise (ASCII/Unicode). Lists are numbers with addresses. Dictionaries are numbers used as keys. Files are numbers stored as bytes. Every data structure ultimately decomposes into numbers, so getting comfortable with numeric operations pays off broadly.

Python's Numeric Design

  • Arbitrary precision integers — no silent overflow, useful for large-scale computations
  • Seamless mixing of numeric types in operations (with implicit conversion)
  • A standard library that covers a lot of common mathematical needs
  • Mature external libraries (NumPy, SciPy, Pandas) built on this foundation

Where This Leads

Understanding Python's fundamentals is what makes the following genuinely accessible later on:

  • NumPy – Multi‑dimensional arrays and vectorised operations
  • SciPy – Scientific computing and optimisation
  • Pandas – Data analysis and manipulation
  • Matplotlib – Data visualisation
  • Scikit‑learn – Machine learning
  • TensorFlow/PyTorch – Deep learning

Each of these libraries is built on the fundamental numeric and structural capabilities this curriculum covers early on.

Beyond Syntax

The most useful thing this curriculum can offer is an understanding of why things work the way they do. Knowing about the GIL helps you choose the right concurrency model. Knowing about duck typing helps you write more flexible code. Knowing about the data model helps you create objects that feel native to Python. That's the difference between code that happens to work and code that's designed to be correct and maintainable.

▶️ How to Run the Python Code

Every snippet on this page is ready to copy, paste, and run

Run Python in your browser — no install needed

All code examples target Python 3.10+. You can run them directly in your browser using the Run button on each snippet, or copy them to a local .py file and execute with python3.

Option 1 — Run in your browser (no setup)

Click the Run button below any code block — it executes inside your browser using Pyodide (Python compiled to WebAssembly).

  • ✓ Works immediately, no installation
  • ✓ First run loads the Python runtime (~10s), subsequent runs are instant
  • ✓ Output appears below the snippet
  • ✓ ⚠️ File operations run in a virtual filesystem — files don't persist. Copy code and run locally for real file work.

Option 2 — Run locally (full control)

Copy the snippet into a .py file and execute it with Python 3.10+.

1. Create a file (e.g. snippet.py)
2. Paste the code
3. Run: python snippet.py

Why Python Is Worth Learning

The language behind a lot of AI, data science, automation, and web work

#1
Most-used language
Stack Overflow 2024
500k+
PyPI packages
for nearly every domain
8–10 wks
Typical time
to cover the foundations
1991
Year Python was
first released publicly

Python has become one of the most widely used programming languages of the last decade. From AI algorithms to office automation scripts to machine-learning models used in healthcare — Python is a common thread across a lot of modern software. Its guiding philosophy — readable code is better than clever code — means beginners tend to produce working programs quickly, while the language still has enough depth to keep experienced developers learning.

Poetic Bytes exists to give anyone motivated access to structured, honest, jargon-free Python education — for free.

  • ✓Write clean, idiomatic Python scripts and automate repetitive tasks
  • ✓Design well-structured classes using OOP: inheritance, encapsulation, polymorphism
  • ✓Handle, clean, and visualise real-world datasets using Pandas and Matplotlib
  • ✓Build and consume RESTful web APIs with Flask or Django
  • ✓Write async code with asyncio for high-performance I/O
  • ✓Organise larger projects with modules, packages, and virtual environments
  • ✓Build a small portfolio of real projects along the way

📚 Complete Python Curriculum

18 topic modules — from your first variable to production AI

🏁

Python Basics

Beginner12 lessons

Variables, assignment, naming conventions, how Python executes code line by line. Zero prior experience needed.

variablesprint()input()comments
Explore →
⌨️

Python Inputs

Beginner8 lessons

Capture user input with input(), handle type conversion, validate data, and build interactive command-line programs.

input()type conversionvalidationinteractive
Explore →
🔢

Python Numbers

Beginner9 lessons

Integers, floats, complex numbers. Arithmetic operators, precedence, the math module, Decimal.

intfloatcomplexmath module
Explore →
⚙️

Python Operators

Beginner10 lessons

Arithmetic, comparison, logical, assignment, bitwise, membership, identity operators, and the walrus operator.

arithmeticlogicalbitwisewalrus :=
Explore →
🔀

Control Flow

Beginner11 lessons

if/elif/else, for, while, break, continue, and Python 3.10+ match statements.

if/eliffor loopswhilematch
Explore →
🔄

Python Loops

Beginner10 lessons

Master for and while loops — iterating over ranges, lists, dicts, and custom iterables with break, continue, and else clauses.

forwhilerange()break/continue
Explore →
✅

Python Booleans

Beginner7 lessons

Truthiness, falsiness, short-circuit evaluation, and bool() with different types.

True/Falsebool()truthy/falsyshort-circuit
Explore →
📝

Python Strings

Beginner14 lessons

f-strings, slicing, re (regex), multiline strings, built-in methods like .split(), .join().

f-stringsslicingregexencode/decode
Explore →
📋

Python Lists

Beginner13 lessons

Comprehensions, sorting, nested lists, append(), pop(), copy vs deepcopy.

comprehensionssortingslicingnested
Explore →
📖

Python Dictionaries

Beginner12 lessons

Key-value stores with O(1) lookup. .get(), defaultdict, dict comprehensions, JSON mapping.

key-valuedefaultdictJSONmerge |
Explore →
🎯

Python Tuples

Beginner8 lessons

Immutable sequences. Packing and unpacking, named tuples, using tuples as dict keys.

immutableunpackingnamedtuplehashable
Explore →
🔵

Python Sets

Beginner9 lessonsNew

Unordered unique collections. Set operations: union, intersection, difference. frozenset and set comprehensions.

unionintersectiondifferencefrozenset
Explore →
🛠️

Python Functions

Intermediate16 lessons

*args, **kwargs, lambdas, closures, higher-order functions, decorators, recursion.

*args/**kwargslambdaclosuresdecorators
Explore →
🏗️

Python Classes

Intermediate14 lessons

Define classes, write __init__, understand self, class vs instance variables, @property.

__init__selfmethods@property
Explore →
📦

Modules & Packages

Intermediate11 lessonsNew

Organise code into reusable files and packages. import mechanics, __init__.py, relative imports, pip, and virtual environments.

import__init__.pypipvenv
Explore →
🛡️

Exception Handling

Intermediate10 lessonsNew

Robust error handling with try/except/else/finally. Custom exceptions, exception chaining, and logging.

try/exceptraisecustom exceptionslogging
Explore →
⚡

Iterators & Generators

Intermediate12 lessonsNew

The iterator protocol, __iter__/__next__, generator functions with yield, generator expressions, and itertools.

yield__iter__itertoolslazy evaluation
Explore →
🔐

Context Managers

Intermediate8 lessonsNew

The with statement, __enter__/__exit__, writing your own context managers with contextlib.

with__enter__contextlibresource management
Explore →
🔒

Encapsulation

OOP9 lessons

Bundle data and methods inside a class. Hide internals with _private, __mangled, and @property.

_private@propertygetter/setter
Explore →
🎭

Abstraction

OOP8 lessons

Expose only what's necessary. abc module, Abstract Base Classes, enforcing contracts between classes.

ABC@abstractmethodinterfaces
Explore →
🌿

Inheritance

OOP11 lessons

Reuse and extend class behaviour. MRO, super(), method overriding, multiple inheritance, mixins.

super()MROoverridemixin
Explore →
🔀

Polymorphism

OOP10 lessons

One interface, many forms. Duck typing, dunder methods, operator overloading, isinstance().

duck typingdunderoperator overload
Explore →
🚀

Async & Concurrency

Advanced13 lessonsNew

Write non-blocking code with asyncio. async def, await, aiohttp, tasks, event loops, and concurrent patterns.

asyncioasync/awaitaiohttptasks
Explore →
🎨

Decorators In Depth

Advanced10 lessonsNew

Factory decorators, class decorators, stacking, functools.wraps, real-world patterns like caching, timing, and auth guards.

functoolswrapslru_cacheclass decorators
Explore →
🔍

Type Hints & mypy

Advanced10 lessonsNew

Annotate everything. TypeVar, Protocol, TypedDict, Literal, Final, generic classes, and running mypy in CI.

TypeVarProtocolGenericmypy
Explore →
📂

File Handling

Intermediate11 lessons

Read, write, append files. pathlib, CSV, JSON, binary files, shutil, os.path, and context managers.

pathlibcsvjsonshutil
Explore →
📄

Reading & Writing Files

Intermediate10 lessonsNew

Dedicated deep dive into reading and writing text, binary, and structured files with open(), pathlib, and safe file handling patterns.

open()read/writepathlibbinary
Explore →
🕷️

Web Scraping

Intermediate12 lessons

Extract structured data from websites. requests, BeautifulSoup, Playwright, Selenium, and handling pagination.

requestsBeautifulSoupPlaywright
Explore →
📊

Data Analysis

Intermediate15 lessons

NumPy, Pandas, Matplotlib, Seaborn. Clean, transform, and visualise real-world datasets.

NumPyPandasMatplotlib
Explore →
🤖

Artificial Intelligence

Advanced14 lessons

LLM APIs (OpenAI, Anthropic), NLP, search algorithms, HuggingFace transformers, prompt engineering.

LLM APIsNLPtransformers
Explore →
🧠

Machine Learning

Expert18 lessons

Supervised & unsupervised learning. scikit-learn, TensorFlow, PyTorch, neural networks.

scikit-learnTensorFlowPyTorch
Explore →
🗣️

Text to Speech

Intermediate9 lessonsNew

Generate spoken audio from text using Python. Explore pyttsx3, Google TTS, Amazon Polly, and build a CLI text-to-speech tool.

pyttsx3Google TTSAmazon Pollyaudio
Explore →
🗺️

Not sure where to start?

The modules are sequenced beginner to advanced, but feel free to jump in anywhere that matches your level.

Start from the beginning

✨ 6 New Modules — Just Added

Expand beyond basics with these essential intermediate and advanced topics

🔵 Python Sets

🔵 Sets — Fast Unique Collections

A set is an unordered collection of unique, hashable objects. It's the fastest Python data structure for membership testing and is ideal for deduplication, intersection, and difference operations.

def demonstrate_sets():
    languages = {"Python", "JavaScript", "Rust", "Python"}
    print("Unique languages:", languages)

    backend  = {"Python", "Go", "Rust"}
    frontend = {"JavaScript", "TypeScript", "Python"}

    print("Union:", backend | frontend)
    print("Intersection:", backend & frontend)
    print("Backend only:", backend - frontend)

    names = ["Alice", "Bob", "Alice", "Charlie", "Bob"]
    unique = list(set(names))
    print("Deduplicated names:", unique)

demonstrate_sets()
💡 x in my_set is O(1). The same check on a list is O(n). For membership testing on large collections, always prefer a set.
📦 Modules & Packages

📦 Organising Code — Modules, Packages & Virtual Environments

As projects grow beyond a single file, Python's module system keeps code organised, testable, and shareable. A module is a single .py file. A package is a folder containing an __init__.py.

Example code removed to avoid import errors in the browser; run this locally with proper file structure.

💡 Avoid installing packages globally. One virtual environment per project keeps dependencies clean and reproducible. Use python -m venv .venv as your first step on every new project.
🛡️ Exception Handling

🛡️ Robust Exception Handling — Beyond try/except

Production Python code anticipates failure at every external boundary. The full try/except/else/finally structure handles each case cleanly. Custom exceptions communicate intent.

class AppError(Exception):
    pass

class ValidationError(AppError):
    def __init__(self, field: str, message: str):
        self.field = field
        super().__init__(f"{field}: {message}")

def parse_user_data(raw: str) -> dict:
    try:
        data = json.loads(raw)
    except json.JSONDecodeError as e:
        raise ValidationError("body", "Invalid JSON") from e
    else:
        return data
    finally:
        print("Parsing attempt finished")
💡 Exception chaining with raise NewError from original preserves the full traceback so you never lose the root cause while adding application-level context.
⚡ Iterators & Generators

⚡ Generators — Lazy, Memory-Efficient Iteration

A generator function uses yield instead of return. It produces values one at a time on demand — no memory is used to hold the full sequence. Essential for processing large files, infinite streams, and data pipelines.

def count_up(start: int, stop: int, step: int = 1):
    current = start
    while current < stop:
        yield current
        current += step

for n in count_up(0, 10, 2):
    print(n, end=" ")

print()

squares = (x**2 for x in range(5))
print("Squares:", list(squares))

def read_large_file(path: str):
    with open(path, encoding="utf-8") as f:
        for line in f:
            yield line.strip()
🚀 Async & Concurrency

🚀 asyncio — Non-Blocking Python

Synchronous code waits for each I/O operation to complete before starting the next. Async code schedules many I/O tasks concurrently, dramatically improving throughput for network-heavy workloads like web scraping, API calls, and database queries.

The asyncio example is omitted because Pyodide's event loop prevents calling asyncio.run(); run this code locally to see it in action.

💡 Use asyncio for I/O-bound concurrency (network, files, databases). Use multiprocessing for CPU-bound work. Never block an async event loop with time.sleep() — use await asyncio.sleep() instead.
🎨 Decorators In Depth

🎨 Factory Decorators & Real-World Patterns

A decorator factory is a function that returns a decorator, allowing parameters. This pattern powers every framework you'll use — Flask routes, pytest fixtures, Django views, and more.

import functools, time

def retry(times: int = 3, delay: float = 1.0):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(1, times + 1):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == times:
                        raise
                    print(f"Attempt {attempt} failed: {e}. Retrying...")
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(times=2, delay=0.5)
def unstable_operation() -> str:
    import random
    if random.random() < 0.6:
        raise ValueError("Random failure!")
    return "Success!"

print(unstable_operation())
✨ New lesson: functools.lru_cache and cache are Python's built-in memoization decorators — zero boilerplate caching for any pure function.
🔍 Type Hints & Static Analysis

🔍 Type Hints — Modern Python Annotations

Type hints document your code and enable static analysis with mypy or pyright. They don't change runtime behaviour but catch entire classes of bugs before the code runs.

from typing import TypeVar, Generic, Protocol
from collections.abc import Sequence

T = TypeVar("T")

class Stack(Generic[T]):
    def __init__(self) -> None:
        self._items: list[T] = []

    def push(self, item: T) -> None:
        self._items.append(item)

    def pop(self) -> T:
        return self._items.pop()

stack: Stack[int] = Stack()
stack.push(42)
stack.push(100)
print("Popped:", stack.pop())

class Drawable(Protocol):
    def draw(self) -> None: ...

def render(shape: Drawable) -> None:
    shape.draw()
✨ Run mypy --strict your_file.py to catch type errors before they reach production. Integrate it in GitHub Actions for automatic checking on every push.

⚠️ 10 Python Mistakes Every Beginner Makes

Learn from thousands of learners — avoid the errors that waste the most time

Every Python beginner tends to make the same set of mistakes — not because they aren't smart, but because Python has a handful of genuinely surprising behaviours that differ from plain logic or from other programming languages. Recognising these traps early saves hours of frustrated debugging. Each mistake below comes with a clear explanation of why it happens and exactly how to fix it.

❌ Mistake 1 — Using a mutable default argument

Default argument values are evaluated once when the function is defined — not each time it is called. If you use a mutable object like a list as a default, all calls that use the default share the same object.

def add_item_bad(item, items=[]):
    items.append(item)
    return items

print(add_item_bad("apple"))
print(add_item_bad("banana"))

def add_item_good(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

print(add_item_good("apple"))
print(add_item_good("banana"))
💡 This applies to dicts, sets, and any other mutable type. Always use None as the default and create the mutable object inside the function body.

❌ Mistake 2 — Confusing == with is

== checks whether two values are equal. is checks whether two names point to the exact same object in memory. For small integers and interned strings Python reuses objects, which can make is appear to work — until it silently does not.

a = 256
b = 256
print("256 is 256:", a is b)

a = 257
b = 257
print("257 is 257:", a is b)
print("257 == 257:", a == b)

if result is None:
    print("Use 'is' for None comparisons")

❌ Mistake 3 — Modifying a list while iterating over it

Changing the size of a list during a for loop causes items to be skipped silently — one of the hardest bugs to spot because Python gives no error, it just produces wrong output.

nums = [1, 2, 3, 4, 5, 6]
for n in nums:
    if n % 2 == 0:
        nums.remove(n)
print("After bad removal:", nums)

nums = [1, 2, 3, 4, 5, 6]
nums = [n for n in nums if n % 2 != 0]
print("After good removal:", nums)

❌ Mistake 4 — Catching too broad an exception

Writing bare except: swallows every error — including typos in variable names, import errors, and keyboard interrupts — making bugs nearly impossible to diagnose.

try:
    result = int("not a number")
except ValueError:
    print("Please enter a valid number")
except TypeError as e:
    print(f"Type error: {e}")
finally:
    print("Cleanup always runs")
💡 If you genuinely need to catch all exceptions, at minimum log the full traceback with logging.exception(e) so the error is not lost.

❌ Mistake 5 — Not understanding variable scope (LEGB rule)

Python resolves variable names in a specific order: Local → Enclosing → Global → Built-in. Forgetting this leads to confusing UnboundLocalError bugs.

count = 0

def increment_bad():
    count += 1

def increment_good():
    global count
    count += 1

def increment_pure(count):
    return count + 1

count = increment_pure(count)
print("Count:", count)

❌ Mistake 6 — Using + to concatenate strings in a loop

Strings in Python are immutable. Every + creates a new string object and copies both into it. Inside a loop over thousands of items, this creates quadratic time complexity.

words = ["Python", "is", "fast", "and", "readable"]

result = ""
for word in words:
    result += word + " "
print(result)

result = " ".join(words)
print(result)

❌ Mistake 7 — Forgetting integers and strings do not auto-convert

Python is strongly typed. Unlike JavaScript, it never silently coerces a number to a string or vice versa. This causes TypeError that surprises beginners from loosely typed languages.

age = 25

print(f"I am {age} years old")

pi = 3.14159265
print(f"Pi to 2 dp: {pi:.2f}")

❌ Mistake 8 — Shallow copy vs deep copy confusion

Assigning a list to a new variable does not copy it — both names point to the same object. Only a deep copy is truly independent.

import copy

original = [[1, 2], [3, 4]]

alias = original
alias[0][0] = 99
print("Original after alias mutation:", original)

deep = copy.deepcopy(original)
deep[0][0] = 0
print("Original after deep copy mutation:", original)

❌ Mistake 9 — Ignoring return values from string methods

Strings are immutable. Methods like .upper(), .strip(), and .replace() return a new string, they do not modify in place.

name = "  glenn  "
name.strip()
print(name)

name = name.strip()
print(name)

text = "hello world"
text = text.upper().replace("WORLD", "PYTHON")
print(text)

❌ Mistake 10 — Writing loops when a built-in would do

Python ships with a rich set of built-in functions and comprehensions. Writing an explicit loop where a one-liner exists is slower and harder to read.

squares = []
for n in range(10):
    if n % 2 == 0:
        squares.append(n ** 2)
print(squares)

squares = [n**2 for n in range(10) if n % 2 == 0]
print(squares)

data = [1, 5, 3, 9, 2]
print("Sum:", sum(data))
print("Max:", max(data))
print("Min:", min(data))
💡 Built-ins like sum(), max(), min(), any(), all(), zip(), enumerate(), and sorted() exist because these patterns appear in almost every Python program. Learn them early.

✅ Python Best Practices — Write Code Like a Pro

The habits that separate readable, maintainable Python from spaghetti code

Knowing the syntax is only half of Python. The other half is knowing the conventions, patterns, and habits that make your code easy to read, debug, and maintain — both for yourself six months from now and for anyone who works with your code. These practices are not opinions; they are the documented conventions of the Python community, codified in PEP 8 and reinforced by years of collective experience.

📏 Follow PEP 8 — Python's Official Style Guide

PEP 8 covers naming conventions, indentation, line length, blank lines, and import ordering. Most professional Python codebases enforce it automatically with tools like black or ruff.

user_name = "Glenn"
def calculate_average(scores): ...

class UserAccount: ...
class DataProcessor: ...

MAX_RETRIES = 3
BASE_URL = "https://api.example.com"

_internal_helper = "not for external use"

📝 Write Docstrings for Every Public Function and Class

Docstrings are first-class documentation that tools like Sphinx, IDE tooltips, and help() can read. A function without a docstring forces the reader to study the implementation to understand intent.

def calculate_bmi(weight_kg: float, height_m: float) -> float:
    if height_m <= 0:
        raise ValueError("Height must be positive")
    return round(weight_kg / height_m ** 2, 2)

🔒 Use Type Hints — Standard Practice by 2026

Python's type hints don't change runtime behaviour, but they dramatically improve readability, enable IDE autocompletion, and allow static type checkers like mypy to catch bugs before your code runs.

from typing import Optional, TypedDict

def find_user(user_id: int) -> Optional[dict]:
    ...

def parse_value(raw: str) -> int | float:
    try:
        return int(raw)
    except ValueError:
        return float(raw)

class Article(TypedDict):
    title: str
    author: str
    word_count: int
    published: bool

🧪 Write Tests — Even Simple Ones

Untested code is broken code waiting to be discovered. Python's built-in unittest and the popular third-party pytest make testing straightforward. Even a handful of tests catches regressions before they reach production.

The pytest example is omitted because Pyodide does not include pytest; run this code locally with pytest installed.

💡 A reasonable rule of thumb: if a bug is worth fixing, write a test that would have caught it. Over time your test suite becomes a safety net that lets you refactor confidently.

📦 Use dataclasses to Eliminate Boilerplate

The @dataclass decorator (Python 3.7+) auto-generates __init__, __repr__, and __eq__ from annotated fields, reducing repetitive code dramatically.

from dataclasses import dataclass, field
from typing import List

@dataclass
class Student:
    name: str
    grade: int
    scores: List[float] = field(default_factory=list)

    @property
    def average(self) -> float:
        return sum(self.scores) / len(self.scores) if self.scores else 0.0

s = Student("Ana", 10)
s.scores.extend([88, 92, 95])
print(s)
print(f"Average: {s.average:.2f}")
@dataclassfield()frozen=Truedefault_factory

🔧 Essential Python Built-in Functions You Must Know

Python ships with 70+ built-in functions — these appear in virtually every real program

One of Python's greatest strengths is how much useful functionality is available without importing anything. These built-in functions are always available, highly optimised in C, and cover the most common programming needs. Knowing them means less code, fewer bugs, and faster programs.

🔢

enumerate() — Loop with index

Returns an iterator of (index, value) pairs. Eliminates the need for a manual counter variable.

fruits = ["apple", "banana", "cherry"]
for i, fruit in enumerate(fruits, start=1):
    print(f"{i}. {fruit}")
🤐

zip() — Combine iterables

Pairs up items from multiple iterables. Perfect for looping over two related lists simultaneously.

names  = ["Ana", "Bob", "Cara"]
scores = [88, 92, 79]
for name, score in zip(names, scores):
    print(f"{name}: {score}")
🔀

sorted() & .sort()

sorted() returns a new sorted list. .sort() sorts in place. Both accept a key function.

words = ["banana", "Apple", "cherry"]
print(sorted(words, key=str.lower))

people = [{"name":"Bob","age":30},{"name":"Ana","age":25}]
people.sort(key=lambda p: p["age"])
print(people)
🗺️

map() & filter()

Apply a function to every item (map), or keep only items matching a condition (filter). Both return lazy iterators.

nums = [1, 2, 3, 4, 5, 6]
doubled = list(map(lambda x: x*2, nums))
evens   = list(filter(lambda x: x%2==0, nums))
print(doubled)
print(evens)
✔️

any() & all()

any() returns True if at least one item is truthy. all() returns True only if every item is truthy. Both short-circuit.

scores = [85, 92, 78, 95, 88]
print(all(s >= 70 for s in scores))
print(any(s >= 90 for s in scores))

required = ["name", "email", "age"]
data = {"name":"Ana", "email":"a@b.com", "age":25}
print(all(k in data for k in required))
📊

isinstance() & type()

isinstance() is the correct way to check an object's type — it handles inheritance. type() gives the exact type without inheritance awareness.

class Animal: pass
class Dog(Animal): pass

d = Dog()
print(isinstance(d, Dog))
print(isinstance(d, Animal))
print(type(d) is Animal)

📂 File Handling & Error Handling in Python

Reading, writing, and processing files safely — skills every Python developer needs daily

Almost every real Python program interacts with files — reading configuration, processing CSVs, writing logs, parsing JSON. Doing this correctly means using context managers, handling exceptions gracefully, and choosing the right encoding.

Run Locally Only — File operations in the browser use a temporary virtual filesystem. Files do not persist and may behave differently than on your machine. For real file work, copy the code and run it with Python on your computer.

📖 Reading and Writing Files Safely

Always use the with statement when opening files. It guarantees the file is closed even if an exception occurs — no resource leaks, no corrupted files.

The file I/O example is omitted because the browser environment lacks the actual files; run this code locally with existing files.

💡 Always specify encoding="utf-8" explicitly. The default encoding varies by platform and can cause mysterious bugs when your code runs on a different machine.

🗂️ pathlib — Modern File Path Handling

The pathlib module (Python 3.4+) provides an object-oriented interface to filesystem paths. It is cleaner, more readable, and cross-platform compared to old-style string manipulation with os.path.

from pathlib import Path

home    = Path.home()
project = Path("myproject")
data    = project / "data" / "input.csv"

print(f"Name: {data.name}")
print(f"Stem: {data.stem}")
print(f"Suffix: {data.suffix}")
print(f"Parent: {data.parent}")
print(f"Exists: {data.exists()}")

for pyfile in Path(".").glob("*.py"):
    print(pyfile)

📋 Working with CSV and JSON

CSV and JSON are the two formats you will encounter most often in real-world Python work — spreadsheet exports, API responses, and configuration files.

import csv, json

rows = [{"name":"Ana","score":95},{"name":"Bob","score":87}]
with open("results.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=["name","score"])
    writer.writeheader()
    writer.writerows(rows)

data = {"user": "Glenn", "level": "advanced", "scores": [95, 88, 92]}
with open("user.json", "w") as f:
    json.dump(data, f, indent=2)

🏛️ Python OOP Deep Dive

Classes, dunder methods, properties, and dataclasses with full examples

🏛️ Object-Oriented Programming

🏗️ Defining Classes & __init__

A class bundles data (attributes) and behaviour (methods). __init__ runs on every new instance. self refers to the instance — not a keyword, just a strong convention.

class BankAccount:
    def __init__(self, owner: str, balance: float = 0.0):
        self.owner   = owner
        self.balance = balance
        self._history = []

    def deposit(self, amount: float):
        if amount <= 0:
            raise ValueError("Amount must be positive")
        self.balance += amount
        self._history.append(f"+{amount}")

    def withdraw(self, amount: float):
        if amount > self.balance:
            raise ValueError("Insufficient funds")
        self.balance -= amount
        self._history.append(f"-{amount}")

    def statement(self) -> str:
        return f"{self.owner}: ${self.balance:.2f}"

acc = BankAccount("Alice", 500)
acc.deposit(200)
acc.withdraw(50)
print(acc.statement())
💡 Every method receives self as its first argument. Python passes it automatically — you never supply it when calling acc.deposit(200).

✨ Dunder Methods (Magic Methods)

Python calls dunder methods automatically in response to built-in operations. Implementing them makes your objects feel native — comparable with ==, printable, addable with +.

class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __len__(self):
        import math
        return int(math.hypot(self.x, self.y))

v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1 + v2)
print(v1 == v2)
print("Length of v1:", len(v1))
__repr____str____add____eq____len____iter____contains__

🎯 @property, @classmethod, @staticmethod

@property turns a method into a computed attribute. @classmethod receives the class for alternative constructors. @staticmethod is a helper namespaced inside the class with no implicit argument.

class Temperature:
    def __init__(self, celsius: float):
        self._celsius = celsius

    @property
    def celsius(self):
        return self._celsius

    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("Below absolute zero!")
        self._celsius = value

    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32

    @classmethod
    def from_fahrenheit(cls, f: float):
        return cls((f - 32) * 5/9)

    @staticmethod
    def is_freezing(celsius: float) -> bool:
        return celsius <= 0

t = Temperature.from_fahrenheit(212)
print(f"Celsius: {t.celsius}")
print(f"Fahrenheit: {t.fahrenheit}")
print(f"Is freezing at -5°C? {Temperature.is_freezing(-5)}")

🧠 How Python Actually Runs Your Code

A look under the hood — what happens between typing python script.py and seeing output

Most tutorials teach syntax without explaining what the interpreter does with it. Understanding this layer helps explain why certain patterns are fast, why others are slow, and why some bugs happen at all.

📦 Bytecode and the CPython Interpreter

When you run a .py file, CPython (the reference implementation most people mean by "Python") first compiles your source into an intermediate form called bytecode — a set of low-level instructions stored in .pyc files. The interpreter then executes this bytecode one instruction at a time in a loop. You can inspect it yourself:

import dis

def add(a, b):
    return a + b

dis.dis(add)
💡 Running dis.dis() on a function shows exactly which bytecode instructions Python generated. It's a useful way to see why, for example, tuple unpacking is fast — it compiles to a handful of direct instructions rather than a general-purpose loop.

🔗 Names, Objects, and Reference Counting

A common early confusion is thinking that variables "contain" values the way boxes contain items. In Python, variables are names bound to objects. Assignment doesn't copy data — it points a name at an existing object in memory.

a = [1, 2, 3]
b = a
print("Same object?", a is b)
print("id(a):", id(a))
print("id(b):", id(b))

b.append(4)
print("a after b.append(4):", a)

Every object keeps a count of how many names point to it. When that count reaches zero, CPython frees the memory immediately — this is reference counting, and it's why Python generally doesn't need you to manage memory by hand. Circular references (an object that indirectly refers to itself) are handled separately by a periodic cyclic garbage collector.

⏱️ Why Some Operations Are Faster Than Others

Understanding the underlying data structures explains real performance differences you'll run into:

  • ✓Lists are backed by contiguous arrays — appending at the end is fast (amortised O(1)), but inserting at the front is O(n) because every element has to shift.
  • ✓Dictionaries and sets are hash tables — lookups are close to O(1) regardless of size, which is why x in my_dict is far faster than x in my_list for large collections.
  • ✓Strings are immutable, so every concatenation with + allocates a new string — this is why repeated concatenation in a loop is quadratic, while "".join(parts) is linear.
  • ✓Tuples are slightly cheaper than lists in both memory and creation time because they don't need to support resizing.
💡 None of this needs to be memorised up front. It becomes useful once you're optimising real code — knowing where to look explains far more than knowing which function is faster.

🧩 Common Algorithmic Patterns in Python

Recognisable shapes that show up across a huge range of problems

Beyond individual language features, a handful of algorithmic patterns come up again and again — in coding interviews, in data processing scripts, and in everyday problem solving. Recognising the pattern is often the hard part; the Python implementation is usually short once you see it.

👉 Two Pointers

Two indices move through a sequence — usually from opposite ends or at different speeds — to avoid a nested loop. Useful for searching sorted data, detecting palindromes, or removing duplicates in place.

def is_palindrome(s: str) -> bool:
    left, right = 0, len(s) - 1
    while left < right:
        if s[left] != s[right]:
            return False
        left += 1
        right -= 1
    return True

print(is_palindrome("racecar"))
print(is_palindrome("python"))

🪟 Sliding Window

A window of fixed or variable size slides across a sequence, keeping a running total or count instead of recomputing from scratch each time. Common for "longest substring" or "maximum sum subarray" style problems.

def max_sum_subarray(nums: list[int], k: int) -> int:
    window_sum = sum(nums[:k])
    best = window_sum
    for i in range(k, len(nums)):
        window_sum += nums[i] - nums[i - k]
        best = max(best, window_sum)
    return best

print(max_sum_subarray([2, 1, 5, 1, 3, 2], 3))

🧮 Hash Map Counting

A dictionary used to count occurrences turns many "find duplicates" or "find the most frequent item" problems into a single linear pass instead of a nested loop.

from collections import Counter

words = "the quick brown fox jumps over the lazy dog the fox runs".split()
counts = Counter(words)
print(counts.most_common(2))
💡 collections.Counter is a dict subclass built exactly for this pattern — reach for it before writing a manual counting loop.

🔁 Recursion & Memoisation

Some problems are naturally defined in terms of smaller versions of themselves. Recursion expresses that directly; memoisation avoids recomputing the same subproblem twice.

from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n: int) -> int:
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

print([fibonacci(n) for n in range(10)])
💡 Without @lru_cache, naive recursive Fibonacci recomputes the same values exponentially many times. The decorator turns it into a fast, linear-time solution with no change to the algorithm's structure.

🏗️ 6 Interactive Real-World Python Projects

Click any project to explore the full walkthrough — each card includes a live demo snippet

Reading about a concept and building something with it are different skills. Each project below is a documented walkthrough with runnable code, step‑by‑step explanations, and a link to the complete guide. Click any card to open the detailed project page, or expand the demo to see the code in action right here.

📊

Personal Finance Tracker Beginner

csvdictfile I/Oaggregation
Read a bank export CSV, categorise transactions automatically, compute monthly spending by category, and generate a text summary report.
# finance_tracker.py transactions = [ {"date":"2026-01-05","desc":"Grocery","amount":-45.20,"cat":"Food"}, {"date":"2026-01-07","desc":"Salary","amount":2500.00,"cat":"Income"}, {"date":"2026-01-10","desc":"Coffee","amount":-4.50,"cat":"Food"}, ] summary = {} for t in transactions: cat = t["cat"] summary[cat] = summary.get(cat, 0) + t["amount"] print("Monthly summary:", summary) # Monthly summary: {'Food': -49.7, 'Income': 2500.0}
🌤️

Weather CLI Tool Beginner

requestsJSONargparseAPI
Build a command-line tool that accepts a city name, fetches live weather data from the OpenWeatherMap API, and displays temperature, conditions, and humidity.
# weather_cli.py (mock output) $ python weather.py --city London 🌍 Weather in London, UK 🌡️ Temperature: 14.5°C (feels like 12.0°C) 💧 Humidity: 72% 🌬️ Wind: 5.2 m/s ✅ Data fetched successfully from OpenWeatherMap API
🕷️

Job Listing Scraper Intermediate

BeautifulSoupCSS selectorspaginationCSV
Scrape a public job board for Python developer roles, extract job title, company, location, and salary, then save to CSV.
# job_scraper.py — mock results Scraped 24 Python jobs from boards.tech Saved to jobs_2026-01-15.csv Sample row: {"title":"Senior Python Developer","company":"DataCorp","location":"Remote","salary":"$140k"} ✅ 24 records saved to CSV
📈

Stock Price Dashboard Intermediate

PandasyfinanceMatplotlibtime series
Fetch historical stock prices, calculate 7‑day and 30‑day moving averages, detect crossover signals, and plot an interactive chart with Matplotlib.
# stocks.py — mock signal detection Ticker: AAPL | Period: 6 months Current price: $187.32 7-day MA: $184.15 | 30-day MA: $181.80 ⚡ Crossover signal: BUY (7-day crossed above 30-day) ✅ Chart saved as aapl_ma.png
🤖

AI-Powered Text Summariser Advanced

LLM APIFlaskprompt engineeringREST
Build a Flask web app that accepts a long article URL or pasted text, sends it to the Anthropic or OpenAI API, and returns a concise bullet‑point summary.
# summarizer.py — mock API response 📄 Article: "Python 3.13 adds experimental free‑threaded mode..." 🤖 Summary (via Claude API): • Python 3.13 introduces a no‑GIL build option • Performance gains up to 25% on multi‑core workloads • The feature is currently experimental, not production‑ready ✅ Summary generated in 2.3s
🗄️

REST API with FastAPI & SQLite Advanced

FastAPISQLAlchemyPydanticJWT
Build a fully functional REST API for a task manager: CRUD endpoints, SQLite database via SQLAlchemy, Pydantic validation, automatic OpenAPI docs, and JWT authentication.
# fastapi_tasks.py — mock API response 🔐 POST /auth/login → {"access_token":"eyJhbGciOiJIUzI1NiIs..."} 📋 GET /tasks → [ {"id":1,"title":"Write project docs","done":false}, {"id":2,"title":"Deploy to Render","done":true} ] ✅ Swagger UI available at /docs ✅ API ready — 4 endpoints, JWT protected
🚀

Each project includes a complete walkthrough

Click any card to open the full guide with step‑by‑step instructions, code, and deployment notes.

Browse the projects

🛠️ The Python Ecosystem — Tools Every Developer Uses

Beyond the language — the libraries, tools, and workflows professional Python developers rely on daily

Learning Python syntax is the beginning, not the end. Professional Python development involves a constellation of tools: package managers, virtual environments, formatters, linters, testing frameworks, and domain libraries. This guide maps the essential tools across every area of Python work.

📦 Package Management — pip, venv, and poetry

Every Python project should live in a virtual environment — an isolated directory that contains its own Python interpreter and packages, completely separate from your system Python.

python -m venv .venv
source .venv/bin/activate
.venv\Scripts\activate

pip install requests pandas flask
pip freeze > requirements.txt
pip install -r requirements.txt

poetry new my-project
poetry add requests
poetry run python main.py

🎨 Code Quality Tools — Black, Ruff, mypy

Modern Python development automates code style. These tools run as pre-commit hooks or in CI/CD pipelines to ensure every line of code that enters the codebase meets a consistent standard.

pip install black
black .

pip install ruff
ruff check .
ruff check --fix .

pip install mypy
mypy main.py
mypy src/

pip install pre-commit
pre-commit install
🌐

Web Frameworks

Flask — lightweight, ideal for APIs. Django — batteries-included for full applications. FastAPI — modern, async-first, automatic OpenAPI docs.

Explore →
📊

Data Science Stack

NumPy for arrays. Pandas for DataFrames. Matplotlib and Seaborn for visualisation. Jupyter for interactive exploration.

Explore →
🤖

ML & AI Libraries

scikit-learn for classical ML. TensorFlow and PyTorch for deep learning. HuggingFace Transformers for NLP and LLMs.

Explore →
🧪

Testing Frameworks

pytest — the standard. unittest — built-in, no install needed. hypothesis — property-based testing. pytest-cov for coverage.

Explore →
⚙️

Automation & CLI

Click and Typer for CLI apps. Schedule for task scheduling. Celery for distributed task queues. Playwright for browser automation.

Explore →
☁️

Deployment & DevOps

Docker for containerisation. GitHub Actions for CI/CD. Render, Railway, Fly.io for free-tier hosting. AWS Lambda for serverless Python.

Explore →

🎯 Python Interview Preparation — Questions & Answers

The most commonly asked Python interview questions at tech companies, with detailed answers

Python interviews range from basic syntax questions to tricky language internals, data structure problems, and system design. Preparation helps a lot here. Below are the questions that appear most frequently across junior, mid-level, and senior Python developer interviews.

A list is mutable — you can add, remove, or change elements after creation. A tuple is immutable — once created it cannot be changed. Tuples are faster to iterate and can be used as dictionary keys (because they are hashable), whereas lists cannot. Use tuples for data that should not change (coordinates, database records) and lists for collections that will be modified.

A generator is a function that uses yield instead of return. It returns a generator object — a lazy iterator that produces values one at a time on demand, rather than computing and storing all values at once. Use generators when working with large datasets that do not fit in memory, producing infinite sequences, or building data pipelines. A generator expression uses negligible memory; the equivalent list comprehension would allocate memory for all items immediately.

The Global Interpreter Lock (GIL) is a mutex in CPython that allows only one thread to execute Python bytecode at a time. Even on multi-core processors, Python threads cannot run truly in parallel for CPU-bound tasks. However, the GIL is released during I/O operations, so threading works well for I/O-bound workloads. For true CPU parallelism use multiprocessing or concurrent.futures.ProcessPoolExecutor. Python 3.13 introduced experimental no-GIL builds.

A @staticmethod receives no implicit first argument — it is essentially a regular function that lives inside a class for organisational purposes. A @classmethod receives the class itself as its first argument (cls). This makes it ideal for alternative constructors — factory methods that create instances in different ways. Use @staticmethod for utilities logically belonging to the class. Use @classmethod when the method needs to know about the class itself, especially for inheritance-aware construction.

Python uses reference counting as its primary memory management strategy. Every object tracks how many names point to it. When the count drops to zero, memory is immediately freed. However, reference counting cannot handle circular references. CPython's cyclic garbage collector runs periodically to detect and break these cycles. For performance-critical code, minimise circular references and use __slots__ to reduce per-object memory overhead.

A decorator is a callable that takes a function as input and returns a new function that wraps the original — extending or modifying its behaviour without changing its source code. @my_decorator above a function is syntactic sugar for func = my_decorator(func). Always use @functools.wraps(func) inside your wrapper to preserve the original function's metadata. Decorators power Flask routes, pytest fixtures, @lru_cache, and authentication everywhere in Python.

A list comprehension eagerly creates a full list in memory — use when you need to iterate multiple times or need random access. A generator expression is lazy — it produces values on demand — use for large datasets or single-pass pipelines. map() also returns a lazy iterator but requires a function object, making it slightly less readable. In modern Python, list comprehensions and generator expressions are preferred for clarity.

Dunder (double underscore) methods — also called magic methods — are how Python implements its data model. When you write a + b, Python calls a.__add__(b). When you write len(obj), Python calls obj.__len__(). By implementing these methods in your own classes, your objects integrate seamlessly with Python's built-in syntax. Key ones: __init__, __repr__, __str__, __eq__, __hash__, __len__, __iter__, __next__, __enter__, __exit__.

asyncio is Python's built-in library for writing concurrent code using the async/await syntax. It is event-loop based — instead of blocking while waiting for I/O, a coroutine yields control back to the event loop, which can run other tasks. Use async Python when your bottleneck is I/O: network requests, database queries, file reads. Do not use it for CPU-bound work — for that, use multiprocessing. The key rule: never call time.sleep() inside an async function — use await asyncio.sleep() instead.

A context manager is any object that implements __enter__ and __exit__. The with statement calls __enter__ on entry and __exit__ on exit — even if an exception is raised. This pattern guarantees cleanup: closing files, releasing locks, committing or rolling back database transactions. You can also create context managers with @contextlib.contextmanager — a generator function where code before yield is the setup and code after is the teardown.

📚 Curated Python Learning Resources

The books, documentation, and practice platforms that complement these lessons

Learning from a single source is never enough. The most effective way to learn Python combines structured lessons with official documentation, books, and hands-on problem solving. Below are resources that consistently hold up well at each stage of the learning journey.

📖

Official Python Documentation

The most authoritative source for any Python question. The tutorial at docs.python.org/3/tutorial is surprisingly readable. The library reference covers every built-in function and standard library module in exhaustive detail.

docs.python.orgofficialalways updated
🐍

Fluent Python (Luciano Ramalho)

A strong book for developers who already know the basics and want to understand Python deeply — data model, functions as objects, OOP idioms, control flow, metaprogramming. The 2nd edition covers Python 3.10+.

bookintermediate–advancedO'Reilly
🎮

LeetCode & HackerRank

Practice data structures and algorithms in Python with immediate feedback. LeetCode's Python 3 tag filters to problems that test Python-specific knowledge. Even 30 minutes of problem-solving practice per day adds up.

problem solvinginterview prepalgorithms
🎯

Real Python (realpython.com)

High-quality tutorials and articles on nearly every Python topic — from beginner walkthroughs to deep dives on CPython internals. Particularly strong on web scraping, REST APIs, testing, async Python, and packaging.

tutorialsarticlesbeginner–advanced
🤝

Python Discord & Reddit r/learnpython

Two of the most active Python learning communities online. Post your code, ask questions, and get answers from experienced developers. Python Discord runs live code reviews and weekly challenges.

communityQ&Amentorship
🧪

Exercism.io — Python Track

85+ Python exercises organised by concept, with human mentor review on every submission. Unlike LeetCode which focuses on algorithms, Exercism focuses on idiomatic, well-structured Python — the style and conventions that matter in a professional codebase.

exercisesmentor reviewidiomatic Python

How This Curriculum Is Built

A few design choices behind every module

⌨️

Runnable Examples

Edit code, see results. No local setup needed — experiment directly in your browser.

🧩

Explained From First Principles

From variable scope to dunder methods, every concept is explained rather than assumed.

📁

Project-First

Build a small portfolio while you learn: CLI tools, web scrapers, data visualisations.

🧭

Career-Relevant Context

Algorithms, interview patterns, and system design basics included throughout.

🔍

Organised for Reference

Indexed and cross-linked so you can find any Python topic quickly, not just read start to finish.

⚡

Kept Up to Date

Modules are revised as Python's standard library and typing system evolve.

Suggested Learning Paths

Structured sequences for different goals

Beginner

Python Foundations

For absolute beginners. Learn the basics, write your first scripts, and build small projects.

  • ✓Zero prerequisites required
  • ✓40 hands-on exercises
  • ✓5 portfolio projects
8 weeks 45 hours
Explore →
Intermediate

Data Handling

Work with real datasets, clean and analyse them, create visualisations. No math degree required.

  • ✓NumPy & Pandas basics
  • ✓Plotting with Matplotlib
  • ✓Real-world datasets
12 weeks 65 hours
Explore →
Advanced

Web & APIs

Build dynamic web apps with Flask or Django, connect to databases, and deploy your projects.

  • ✓Flask & Django basics
  • ✓Database integration
  • ✓RESTful API design
16 weeks 85 hours
Explore →
New

Async & Advanced Python

Master modern Python: async/await, type hints, generators, context managers, and clean architecture.

  • ✓asyncio & aiohttp
  • ✓Type hints with mypy
  • ✓Advanced decorators
10 weeks 55 hours
Explore →

Python vs. Other Languages

Understanding where Python fits and when to choose it

FeaturePython 🐍JavaScriptJavaC++
Learning CurveVery Gentle ✓ModerateSteepVery Steep
Common UsesAI/ML, Data, Scripts ✓Web Front-endEnterprise AppsSystems / Games
ReadabilityExcellent ✓GoodVerboseComplex
Async Supportasyncio ✓Native (Promises)Project LoomBoost.Asio
AI/ML EcosystemVery large ✓LimitedModerateLimited
Raw SpeedInterpretedFast (JIT)Fast (JVM)Fastest ✓

Python's own interpreted speed is offset in practice by NumPy and PyTorch calling optimised C or C++ code under the hood.

A Suggested Study Roadmap

From your first script to comfortable, professional-level Python

1
🌱 Foundations (Weeks 1–8) Beginner

Variables, data types, conditionals, loops, functions, file I/O, error handling. Goal: write your first working script that automates a real task.

2
🏗️ OOP + Modules (Weeks 8–16) Intermediate

Classes, inheritance, dunder methods, packages, virtual environments. New: generators, context managers, exception hierarchies.

3
⚡ Advanced Python (Weeks 16–24) Advanced

asyncio, decorators, type hints, mypy. Write well-typed, concurrent Python that reads clearly.

4
🚀 Choose a Specialisation (Weeks 24+)
📊

Data Science

NumPy, Pandas, Scikit-learn

🌐

Web Dev

Flask, Django, FastAPI

⚡

Automation

Selenium, Playwright

🤖

AI / ML

TensorFlow, HuggingFace

📖 Python Key Concepts Glossary

Essential terms explained in plain language — click any card to jump to the related section

variable

A named container holding a value. Python infers the type automatically — no declaration needed.

function

A reusable block defined with def. First-class objects — you can pass them around like variables.

generator

A function using yield to produce values lazily. Memory-efficient for large datasets and infinite sequences.

decorator

A function that wraps another to extend its behaviour without modifying it. Written with @.

async/await

Keywords that mark non-blocking coroutines. await yields control back to the event loop while waiting for I/O.

context manager

The with statement pattern. Guarantees setup and teardown (open/close, acquire/release) even on errors.

Protocol

A structural type hint. Any class implementing the required methods satisfies the Protocol — no explicit inheritance needed.

dataclass

A decorator that auto-generates __init__, __repr__, and __eq__ from annotated fields. Less boilerplate.

About the Author

Who wrote and maintains this curriculum

👨‍💻

Glenn Junsay Pansensoy

Python Developer, Writer & Teacher — Diplahan, Zamboanga Sibugay, Philippines

I picked up Python while automating small tasks and kept going from there. My approach to writing this curriculum: skip the jargon, write real code, and always explain the why behind a concept, not just the syntax.

  • Independent Python developer and technical writer
  • Self-taught, with a focus on data science topics
  • Maintains this curriculum and Poetic Codes, a long-form essay publication

🐞 Debugging Python Code Effectively

Reading errors, using the built-in debugger, and forming a repeatable process for finding bugs

Debugging is a skill on its own, separate from knowing the language. Most of the time spent "learning to code" is really spent learning to figure out why code doesn't do what you expected. This section covers the tools and habits that make that process faster.

📜 Reading a Traceback Top to Bottom (Then Bottom to Top)

A traceback lists the chain of function calls that led to an error, in the order they happened. Beginners often read it top-down like normal text, but the most useful information — the actual error type and message — is at the very bottom. Read the last line first, then walk upward to see which call led there.

def get_first_item(items):
    return items[0]

def process(data):
    return get_first_item(data)

process([])
💡 Running this raises IndexError: list index out of range with a traceback showing process() called get_first_item(), which failed on the indexing line. The bottom line tells you what went wrong; the frames above it tell you where in your call chain to look.

🔎 Recognising Common Exception Types on Sight

Learning to recognise an exception type at a glance saves time — each one narrows down the search significantly before you even look at the message.

  • ✓NameError — a name is used before it's defined, usually a typo or a variable used outside its scope
  • ✓TypeError — an operation was applied to a type it doesn't support, like adding a string to an integer
  • ✓ValueError — the type is correct but the value isn't acceptable, like int("abc")
  • ✓KeyError — a dictionary lookup used a key that doesn't exist
  • ✓IndexError — a sequence was accessed with an index outside its valid range
  • ✓AttributeError — code tried to use a method or attribute an object doesn't have, often from a typo or a None where an object was expected

🖨️ Print Debugging — Simple, and Still Worth Doing Well

Sprinkling print() statements is often dismissed as unsophisticated, but done deliberately it's a fast way to confirm assumptions. The key is printing enough context to be useful, not just a bare value.

def calculate_discount(price, percent):
    print(f"[calculate_discount] price={price!r} percent={percent!r}")
    discount = price * (percent / 100)
    print(f"[calculate_discount] discount={discount!r}")
    return price - discount

print(calculate_discount(100, 15))
💡 Using !r in an f-string shows the repr() of a value, which distinguishes "15" (a string) from 15 (an integer) — a common source of confusing bugs that a plain print can hide.

🛑 The Built-in Debugger — breakpoint()

Python 3.7+ includes a built-in breakpoint() function that drops you into an interactive debugger (pdb) at that exact line. It's more powerful than print debugging because you can inspect and change variables live, step through code line by line, and explore the call stack.

def total_price(items):
    total = 0
    for item in items:
        breakpoint()  # execution pauses here
        total += item["price"] * item["qty"]
    return total

Once paused, a few commands cover most needs: n (next line), s (step into a function call), c (continue running), p variable_name (print a variable), and q (quit the debugger).

⚠️ breakpoint() requires an interactive terminal, so it won't pause execution inside the browser runner on this page — try it locally to see it in action.

🧭 A Repeatable Process for Chasing Down a Bug

Random trial and error tends to take longer than a deliberate process. A reasonably reliable sequence:

  1. Reproduce the bug consistently before trying to fix anything — an intermittent bug you can't reliably trigger is much harder to confirm as fixed.
  2. Read the full traceback, bottom to top, and identify the exact line that raised the exception.
  3. Form a specific hypothesis about what the value of a variable is at that point, rather than a vague sense that "something's wrong."
  4. Check that hypothesis directly — with a print statement, breakpoint(), or by running just that line in isolation.
  5. Fix the actual cause, not just the symptom — a try/except that silently swallows the error usually just delays the same bug.
  6. Add a small test or assertion that would have caught this case, so it doesn't come back unnoticed.

🧪 Writing Your First Unit Tests

Using Python's built-in unittest module to catch bugs before they reach production

A test is just code that checks other code. You don't need a large framework to start — Python's standard library includes unittest, which is enough to build the habit before reaching for pytest later.

✅ A Minimal Test Case

A test case is a class that inherits from unittest.TestCase. Each method starting with test_ is run independently, and assertEqual, assertTrue, and similar methods report a clear failure message when something doesn't match.

import unittest

def apply_discount(price: float, percent: float) -> float:
    if not 0 <= percent <= 100:
        raise ValueError("percent must be between 0 and 100")
    return round(price * (1 - percent / 100), 2)

class TestApplyDiscount(unittest.TestCase):
    def test_typical_discount(self):
        self.assertEqual(apply_discount(100, 20), 80.0)

    def test_zero_percent_returns_original_price(self):
        self.assertEqual(apply_discount(50, 0), 50.0)

    def test_invalid_percent_raises(self):
        with self.assertRaises(ValueError):
            apply_discount(100, 150)

runner = unittest.TextTestRunner(verbosity=2)
suite = unittest.TestLoader().loadTestsFromTestCase(TestApplyDiscount)
runner.run(suite)
💡 This example runs as-is using the browser's Run button — unittest is part of the standard library, so it's available without installing anything, unlike pytest.

🎯 What Makes a Good Test

  • ✓Test one behaviour per method — a failing test name should tell you roughly what broke without reading the assertion
  • ✓Cover the typical case, an edge case (empty input, zero, boundary values), and an error case
  • ✓Keep tests independent — one test's outcome should never depend on another test running first
  • ✓Prefer assertRaises over manually catching an exception and checking a flag
  • ✓Name test methods descriptively — test_negative_percent_raises is more useful than test_2

🏗️ setUp and tearDown

When several tests need the same starting state, setUp() runs before every test method and tearDown() runs after — useful for resetting objects, closing files, or cleaning up temporary state.

import unittest

class ShoppingCart:
    def __init__(self):
        self.items = []

    def add(self, name, price):
        self.items.append((name, price))

    def total(self):
        return sum(price for _, price in self.items)

class TestShoppingCart(unittest.TestCase):
    def setUp(self):
        self.cart = ShoppingCart()

    def test_empty_cart_total_is_zero(self):
        self.assertEqual(self.cart.total(), 0)

    def test_total_sums_item_prices(self):
        self.cart.add("Book", 12.5)
        self.cart.add("Pen", 1.5)
        self.assertEqual(self.cart.total(), 14.0)

runner = unittest.TextTestRunner(verbosity=2)
suite = unittest.TestLoader().loadTestsFromTestCase(TestShoppingCart)
runner.run(suite)
💡 Because setUp() runs fresh before each test, test_empty_cart_total_is_zero and test_total_sums_item_prices each get their own brand-new ShoppingCart — one test can't accidentally leave state that affects the other.

Frequently Asked Questions

Straightforward answers about the curriculum

No. The beginner path starts from absolute zero. We explain every concept — including what a variable is, why indentation matters, and how the computer actually runs your code.

Yes. Every lesson, code example, pathway, and community feature is free to use. There are no premium tiers or paywalled content — the site is supported by ads.

Seven new modules: Python Sets, Modules & Packages, Exception Handling (in depth), Iterators & Generators, Async & Concurrency (asyncio), Decorators In Depth, and Type Hints & mypy. They cover the gaps between beginner basics and more advanced, production-style Python.

Most learners complete the foundations path in about 8–10 weeks, studying 5–7 hours per week. Reaching a comfortable, job-relevant level generally takes several more months of consistent practice beyond that, including the advanced modules.

Nothing to get started. Every code example runs in your browser. When you're ready to code locally, there's a short guide to installing Python 3 and VS Code — about 10 minutes total.

All content targets Python 3.10+, with a focus on 3.12+ features including improved error messages, performance improvements, and modern typing syntax. We do not cover Python 2, which reached end-of-life in 2020.

Start with the fundamentals and OOP first. async is an intermediate-to-advanced topic best learned once you're comfortable with functions, classes, and exception handling. Our async module is placed accordingly in the curriculum.

Roles commonly using Python include Data Scientist, ML Engineer, Backend Developer, Data Analyst, DevOps Engineer, and QA Automation Engineer. This content is designed with those real-world uses in mind, including type hints and async patterns that show up often in production code.

📦 Python Standard Library — The Hidden Treasure

Python ships with a vast standard library. These modules solve real problems without any pip install

Python's motto "batteries included" refers to its large standard library. Before reaching for a third-party package, it's worth checking whether the standard library already solves your problem. Below are the standard library modules you will encounter regularly in real-world Python development.

📅

datetime — Dates and Times

Parse, format, and perform arithmetic on dates and times. The most important module for any application dealing with scheduling, logging, or user-facing time values.

🔗

collections — Specialised Containers

Counter, defaultdict, OrderedDict, deque, and namedtuple — supercharged versions of Python's built-in data structures.

🔁

itertools — Iterator Building Blocks

Lazy combinators for efficient looping. chain, cycle, islice, groupby, product, combinations, permutations — used constantly in data processing.

🔧

functools — Higher-Order Functions

lru_cache for memoisation, reduce for fold operations, partial for partial application, and wraps for writing correct decorators.

🔍

re — Regular Expressions

Pattern matching, extraction, and substitution with regular expressions. Essential for text processing, form validation, log parsing, and web scraping.

🧵

threading & multiprocessing

Use threading for I/O-bound concurrency (the GIL is released during I/O). Use multiprocessing for CPU-bound parallelism — each process gets its own Python interpreter and GIL.

⚙️

argparse — Command-Line Interfaces

Build professional CLI tools with typed arguments, help text, subcommands, and default values — all from the standard library with no third-party dependency.

📝

logging — Production-Grade Logging

Avoid relying on print() for debugging in production code. The logging module provides levels (DEBUG, INFO, WARNING, ERROR, CRITICAL), file handlers, formatters, and structured output.

🌍 Python Across Industries — Real-World Applications

How Python is used in different fields — and what you need to learn for each one

Python's versatility means the fundamentals you learn in this course apply directly to medicine, finance, journalism, education, and beyond. Understanding which libraries and patterns matter in your target field helps you focus your learning on what those roles actually need day to day.

🏥

Healthcare & Bioinformatics

Python is used for genomics analysis, medical imaging with SimpleITK, clinical trial data processing, and drug discovery pipelines. Libraries like BioPython handle DNA/protein sequences. Machine learning models built with scikit-learn and PyTorch support diagnostic imaging research.

BioPythonscikit-learnpandasJupyter
💰

Finance & Quantitative Trading

Hedge funds, investment banks, and fintech companies use Python for algorithmic trading, risk modelling, options pricing, and portfolio optimisation. pandas handles time-series data. NumPy powers vectorised mathematical operations. Libraries like QuantLib handle derivatives pricing.

pandasNumPyyfinanceQuantLib
📰

Journalism & Data Journalism

Newsrooms use Python to work with public records, analyse election data, build interactive visualisations, and process structured datasets for reporting. BeautifulSoup, pandas, and Altair are common tools for data journalists.

BeautifulSouppandasAltairOpenAI API
🎓

Education & EdTech

Python is widely taught in universities and coding bootcamps. EdTech platforms often build backends with Django or FastAPI. Adaptive learning systems use ML models to personalise content, and Jupyter Notebooks are widely used for interactive teaching.

DjangoFastAPIJupyterscikit-learn
🏭

Manufacturing & IoT

Python runs on Raspberry Pi and embedded systems for sensor data collection, predictive maintenance, and quality control. MQTT libraries handle IoT messaging. OpenCV powers computer vision for automated defect detection on production lines.

Raspberry PiOpenCVMQTTNumPy
🔬

Scientific Research

Python is a common choice in scientific computing today. The SciPy ecosystem — NumPy, SciPy, Matplotlib, and Jupyter — is used in physics simulations, climate modelling, astronomy, and materials science across many research institutions.

NumPySciPyMatplotlibAstroPy

Choosing a Direction

The Python fundamentals covered here apply to all of these fields. Once you complete the foundations path, picking one specialisation and going deep — learning the domain-specific libraries, reading a bit of the field's literature, and building a couple of small projects — tends to be more useful than trying to cover everything at once.

💡 Python Reads Like Pseudocode — Here's Why That Matters

Understanding Python's design philosophy and why readable code is worth caring about

Guido van Rossum designed Python around one overriding principle: code is read far more often than it is written. Python's syntax leans on meaningful indentation, English-like keywords, and a preference for clarity over cleverness. Compare the same algorithm across languages:

🔍 Same Algorithm, Different Languages

Finding all prime numbers up to N using the Sieve of Eratosthenes:

def sieve_of_eratosthenes(limit: int) -> list[int]:
    is_prime = [True] * (limit + 1)
    is_prime[0] = is_prime[1] = False

    for i in range(2, int(limit**0.5) + 1):
        if is_prime[i]:
            for multiple in range(i*i, limit + 1, i):
                is_prime[multiple] = False

    return [n for n, prime in enumerate(is_prime) if prime]

primes = sieve_of_eratosthenes(50)
print("Primes up to 50:", primes)
💡 Notice how the variable names (is_prime, multiple), the loop structure, and the list comprehension at the end all read like a description of the algorithm itself. This is the Python ideal: code that documents itself.

🐍 The Zen of Python — 19 Guiding Principles

Running import this in any Python interpreter prints the Zen of Python — 19 aphorisms that guide Python's design. The most relevant ones for everyday development emphasise clarity, simplicity, and explicitness over implicit magic.

import this

✍️ Pythonic Code vs. Non-Pythonic Code

Writing "Pythonic" code means leaning on Python's own idioms rather than translating patterns from another language line by line. The difference shows up in both readability and, sometimes, performance.

items = [1, 2, 3, 4, 5, 6]

i = 0
result = []
while i < len(items):
    if items[i] % 2 == 0:
        result.append(items[i] * 2)
    i += 1

result = [item * 2 for item in items if item % 2 == 0]

a, b = 10, 20
temp = a
a = b
b = temp

a, b = b, a

my_dict = {"key": "value"}
if "key" in my_dict:
    value = my_dict["key"]
else:
    value = "default"

value = my_dict.get("key", "default")

📋 Python Quick-Reference Cheat Sheet

The most important Python syntax patterns at a glance — bookmark this page

🔡 Data Types & Operators
Description: Python's built-in types include int, float, str, bool, and None. Operators cover arithmetic (+, -, *, /, //, %, **), comparison, logical, and assignment. The walrus operator := (Python 3.8+) assigns and returns a value in one expression. f-strings (Python 3.6+) are the recommended way to format strings.
x: int     = 42
y: float   = 3.14
s: str     = "Hello, Python!"
b: bool    = True
n          = None

print(10 // 3)
print(10 %  3)
print(2 ** 8)

n = 0
while (n := n + 1) < 5:
    print(f"n is {n}")

name, score = "Glenn", 98.5
print(f"{name}: {score:.1f}%")
print(f"{1_000_000:,}")
print(f"{'hello':>10}")
📋 Collections Cheat Sheet
Description: Python's core data structures: lists (ordered, mutable), dictionaries (key‑value, mutable), sets (unordered, unique), and tuples (ordered, immutable). Master these for efficient data handling. Comprehensions provide a concise way to build lists, dicts, and sets.
lst = [1, 2, 3, 4, 5]
lst.append(6)
lst.insert(0, 0)
lst.pop()
lst.sort(reverse=True)
print(lst[1:4])
print(lst[::-1])

d = {"a": 1, "b": 2}
d["c"] = 3
print(d.get("z", 0))
d = d | {"d": 4}
print({k: v for k, v in d.items() if v > 1})

s = {1, 2, 3}
s.add(4)
s.discard(1)
print({1,2,3} & {2,3,4})
print({1,2,3} | {3,4,5})
print({1,2,3} - {2,3})

t = (1, 2, 3)
a, b, c = t
first, *rest = t
print(first, rest)
🔄 Control Flow Cheat Sheet
Description: Control flow determines the order in which statements execute. if/elif/else for branching, for and while for loops. Python 3.10+ adds match (pattern matching) for expressive branching. The else clause on loops runs only if the loop completed without a break.
x = 42
result = "positive" if x > 0 else "non-positive"

match x:
    case 0:
        print("zero")
    case n if n > 0:
        print(f"positive: {n}")
    case _:
        print("negative")

for i, val in enumerate(["a","b","c"], start=1):
    print(i, val)

for x, y in zip([1,2,3], [4,5,6]):
    print(x + y)

target = 7
for n in range(2, target):
    if target % n == 0:
        print("not prime")
        break
else:
    print("prime")
🛠️ Functions Cheat Sheet
Description: Functions are defined with def. Python supports positional-only (/), positional-or-keyword, keyword-only (*), default values, variable-length arguments (*args, **kwargs). lambda creates anonymous functions. Use functools.partial for partial application.
def demo(pos_only, /, normal, *, kw_only, default=10):
    print(pos_only, normal, kw_only, default)

def flexible(*args, **kwargs):
    for a in args:
        print(a)
    for k, v in kwargs.items():
        print(f"{k}={v}")

square   = lambda x: x**2
evens    = list(filter(lambda x: x%2==0, range(10)))
doubled  = list(map(lambda x: x*2, range(5)))

args   = [1, 2, 3]
kwargs = {"sep": ", ", "end": "!\n"}
print(*args, **kwargs)
🧘 Zen of Python
Description: The Zen of Python is a set of 19 guiding principles that shape Python's design. They emphasise readability, simplicity, and explicitness. You can view them by running import this. Core aphorisms include "Beautiful is better than ugly", "Explicit is better than implicit", "Simple is better than complex", and "Readability counts".
import this
🐍 Pythonic vs. Non-Pythonic
Description: "Pythonic" code leverages language idioms for clarity and performance. Examples: list comprehensions over explicit loops with counters, tuple unpacking for swapping, dict.get() for safe lookups. Non-Pythonic code often imitates patterns from other languages and is harder to read.
items = [1, 2, 3, 4, 5, 6]

i = 0
result = []
while i < len(items):
    if items[i] % 2 == 0:
        result.append(items[i] * 2)
    i += 1

result = [item * 2 for item in items if item % 2 == 0]

a, b = 10, 20
temp = a
a = b
b = temp

a, b = b, a

my_dict = {"key": "value"}
if "key" in my_dict:
    value = my_dict["key"]
else:
    value = "default"

value = my_dict.get("key", "default")

🕰️ A Brief History of Python

How Python went from a Christmas project to one of the world's most widely used programming languages

Understanding where Python came from helps explain why it's designed the way it is — and why its community formed the way it did. Python's history is a story of patient, deliberate development and good timing.

📅 Python's Timeline

print("1989 — Guido van Rossum begins writing Python over Christmas")
print("1991 — Python 0.9.0 released publicly on Usenet")
print("1994 — Python 1.0 released")
print("2000 — Python 2.0 released")
print("2008 — Python 3.0 released")
print("2020 — Python 2 officially reached end-of-life")
print("2021 — Python 3.10: match/case, better errors")
print("2022 — Python 3.11: significant interpreter speedups")
print("2023 — Python 3.12: f-string improvements")
print("2024 — Python 3.13: experimental free-threaded mode")
💡 Guido van Rossum named Python after Monty Python's Flying Circus — not the snake. The snake logo came later.
🐍

Why Python Spread So Widely

Python succeeded where some other "easy" languages didn't scale as well, because it never traded away power for simplicity. A beginner's first script and a large production system can look structurally similar. The language scales with your skill level in a way not every language manages.

🤝

The PSF & Community

The Python Software Foundation (PSF) is a non-profit that holds the intellectual property rights to Python. It funds PyCon conferences, grants to educators, and infrastructure for PyPI. The community's culture around inclusivity and mentorship is a deliberate, ongoing effort, not an accident.

🔮

Where Python Is Headed

With the JIT compiler work started in 3.13 and the experimental no-GIL mode, Python is actively addressing its traditional weakness around raw speed. Later releases aim to make Python more competitive with compiled languages for CPU-bound workloads, without giving up readability.

🏁 You've Built a Solid Foundation

Working through this curriculum — even partway — is a genuinely useful step, and one worth recognising.

✅ What This Curriculum Covers

  • The fundamentals: variables, data types, control flow, functions
  • Python's data structures: lists, dicts, sets, tuples
  • Object‑oriented programming: classes, inheritance, polymorphism, encapsulation
  • Advanced topics: generators, decorators, async/await, type hints
  • Real‑world skills: file I/O, web scraping, data analysis, LLM APIs
  • A small portfolio of projects that demonstrate real problem‑solving
  • The ecosystem: pip, venv, pytest, black, mypy, and more

📚 Treat It as a Reference, Too

This curriculum is meant to be a living resource, not just a one-time read. Bookmark it, revisit sections as needed, and treat the code examples as something to experiment with, not just read — modifying and breaking things is often how concepts actually stick.

🚀 Where This Can Lead

The fundamentals here are the entry point to several directions:

  • Data Analysis – Process and analyse datasets with Pandas and NumPy
  • Machine Learning – Build predictive models with scikit‑learn and TensorFlow
  • Web Development – Create APIs and full‑stack applications with Flask, Django, FastAPI
  • Automation – Write scripts that save real time on manual work
  • Scientific Computing – Simulate, model, and visualise complex systems
  • AI Engineering – Work with LLMs, NLP, and computer vision

💌 A Note From the Author

This curriculum was put together by Glenn Junsay Pansensoy. The goal, as much as possible, has been to make it:

  • Comprehensive — covering the fundamentals thoroughly
  • Interactive — learning by doing, not just reading
  • Practical — tied to real applications, not abstract exercises
  • Free — accessible to anyone, anywhere, without a paywall

If any part of this helped, sharing it with someone else who's learning is genuinely appreciated.

🌟 A Few Reminders

  • Use the right tool for the job — not every problem needs the most advanced feature
  • Know your tools — the standard library solves more than it gets credit for
  • Test your assumptions — code isn't correct until it's been checked
  • Understand the theory behind the code, not just the syntax
  • Consistency beats intensity — regular, smaller sessions tend to work better than occasional long ones

🙏 Thanks for Reading This Far

Thank you for spending time with this material. The skills covered here tend to transfer well — across other languages and across whatever domain you end up working in.

Poetic Bytes · 2026 · Written and maintained by Glenn Junsay Pansensoy

🗣️ Learner Voices

No comments yet — your voice starts the conversation.