|$ curl https://forge-ai.dev/api/markdown?path=docs/python
$cat docs/python-—-0-to-hero.md
updated Recently·55 min read·published

Python — 0 to Hero

PythonBeginner to Advanced🎯Free Tools
Introduction

Python is a high-level, interpreted programming language created by Guido van Rossum and first released in 1991. It emphasizes code readability through significant indentation and a clean syntax that lets developers express concepts in fewer lines than languages like C++ or Java.

Python's design philosophy, summarized in the Zen of Python (PEP 20), includes principles like "Beautiful is better than ugly," "Explicit is better than implicit," and "Simple is better than complex." These principles guide the language's evolution and community practices.

Today, Python is one of the most popular programming languages in the world, powering web applications (Django, Flask), data science (NumPy, Pandas), machine learning (PyTorch, TensorFlow), automation, scripting, and more. This guide takes you from absolute zero to production-level Python.

Your First Python Program

Every language journey starts with Hello, World. Python makes it trivial:

hello.py
Python
1# This is a comment — Python ignores everything after #
2print("Hello, World!") # Output: Hello, World!
3
4# Python 3 is the current version. Check yours:
5import sys
6print(sys.version) # e.g., 3.12.0

info

Python source files use the .py extension. Run them with python3 hello.py in your terminal. On Windows, python hello.py may also work.
Variables & Data Types

Python is dynamically typed — you don't declare types explicitly. A variable is created the moment you assign a value to it.

variables.py
Python
1# Variables — no type declaration needed
2name = "Alice" # str
3age = 30 # int
4height = 5.8 # float
5is_student = True # bool
6hobbies = None # NoneType — absence of value
7
8# Dynamic typing: same variable can hold different types
9thing = 42
10print(type(thing)) # <class 'int'>
11thing = "now a string"
12print(type(thing)) # <class 'str'>
13
14# Type hints (Python 3.5+) — documentation, NOT enforced
15def greet(name: str) -> str:
16 return f"Hello, {name}"
TypeExampleMutable?
int42, -1, 0No
float3.14, 1e10, float('inf')No
str"hello", 'world'No
boolTrue, FalseNo
list[1, 2, 3]Yes
tuple(1, 2, 3)No
dict{'a': 1, 'b': 2}Yes
set{1, 2, 3}Yes

best practice

Use type hints for function signatures and public APIs. They improve readability, enable IDE autocompletion, and work with tools like mypy for optional static analysis.
Strings & Formatting

Strings are sequences of Unicode characters. Python treats them as immutable sequences with rich methods.

strings.py
Python
1# String creation
2s1 = "double quotes"
3s2 = 'single quotes'
4s3 = """multi-line
5strings are useful
6for docstrings and long text"""
7
8# f-strings (Python 3.6+) — preferred way to format
9name = "Alice"
10age = 30
11print(f"{name} is {age} years old")
12# → Alice is 30 years old
13
14# Expressions inside f-strings
15print(f"{2 ** 10}") # → 1024
16
17# String methods
18text = " hello, world! "
19print(text.strip()) # → "hello, world!"
20print(text.title()) # → " Hello, World! "
21print(text.split(",")) # → [" hello", " world! "]
22print(",".join(["a", "b"])) # → "a,b"
23print(text.upper()) # → " HELLO, WORLD! "
24
25# Slicing — one of Python's best features
26msg = "Python"
27print(msg[0]) # → P (first char)
28print(msg[-1]) # → n (last char)
29print(msg[1:4]) # → yth (indices 1..3)
30print(msg[::-1]) # → nohtyP (reversed)
Control Flow

Python uses indentation (4 spaces is the convention) to define code blocks. No braces, no end keywords — just whitespace.

control_flow.py
Python
1# Conditionals
2x = 10
3if x > 0:
4 print("positive")
5elif x == 0:
6 print("zero")
7else:
8 print("negative")
9
10# Truthiness — values that evaluate to False:
11# False, None, 0, 0.0, "" (empty string), [] (empty list), {} (empty dict)
12if not []:
13 print("empty list is falsy") # this runs
14
15# Ternary (conditional expression)
16status = "adult" if age >= 18 else "minor"
17
18# For loop — iterate over any iterable
19for i in range(5): # 0, 1, 2, 3, 4
20 print(i)
21
22# Enumerate — get index and value
23for idx, val in enumerate(["a", "b", "c"]):
24 print(idx, val)
25
26# While loop
27count = 0
28while count < 3:
29 print(count)
30 count += 1
31
32# Break, continue, else on loops
33for n in range(10):
34 if n == 3:
35 continue # skip 3
36 if n == 7:
37 break # stop at 7
38 print(n)
39else:
40 print("loop completed without break") # won't run if break hit
41
42# Match statement (Python 3.10+) — pattern matching
43def describe(value):
44 match value:
45 case 0:
46 return "zero"
47 case int(x) if x > 0:
48 return f"positive int: {x}"
49 case [a, *rest]:
50 return f"list starting with {a}"
51 case _:
52 return "something else"
Lists (Arrays)

Lists are ordered, mutable collections. They can hold elements of different types and support indexing, slicing, and rich methods.

lists.py
Python
1# Creating lists
2nums = [1, 2, 3, 4, 5]
3mixed = [1, "hello", 3.14, True]
4nested = [[1, 2], [3, 4]]
5
6# Indexing and slicing
7print(nums[0]) # → 1
8print(nums[-1]) # → 5
9print(nums[1:3]) # → [2, 3]
10print(nums[::2]) # → [1, 3, 5]
11
12# Common methods
13nums.append(6) # [1, 2, 3, 4, 5, 6]
14nums.extend([7, 8]) # [1, 2, 3, 4, 5, 6, 7, 8]
15nums.insert(0, 0) # [0, 1, 2, ...]
16nums.pop() # removes and returns last element
17nums.remove(3) # removes first occurrence of 3
18nums.sort() # in-place sort
19nums.reverse() # in-place reverse
20
21# List comprehensions — Pythonic and fast
22squares = [x ** 2 for x in range(10)]
23evens = [x for x in range(20) if x % 2 == 0]
24matrix = [[i + j for j in range(3)] for i in range(3)]
Dictionaries

Dictionaries store key-value pairs. Keys must be hashable (immutable types like strings, numbers, tuples). As of Python 3.7, dictionaries maintain insertion order.

dicts.py
Python
1# Creating dicts
2user = {"name": "Alice", "age": 30, "active": True}
3
4# Alternative constructors
5dict(name="Bob", age=25) # from keywords
6dict([("a", 1), ("b", 2)]) # from pairs
7{x: x ** 2 for x in range(5)} # dict comprehension
8
9# Accessing
10print(user["name"]) # → Alice (KeyError if missing)
11print(user.get("email")) # → None (safe access)
12print(user.get("email", "N/A")) # → N/A with default
13
14# Modifying
15user["email"] = "alice@example.com"
16user.update({"age": 31, "city": "NYC"})
17
18# Iteration
19for key in user: # keys by default
20 print(key)
21for val in user.values(): # values
22 print(val)
23for k, v in user.items(): # both
24 print(k, v)
25
26# Merging (Python 3.9+)
27merged = {**dict1, **dict2} # spread operator
28merged = dict1 | dict2 # pipe operator
29
30# Default dict
31from collections import defaultdict
32counts = defaultdict(int) # missing key → 0
33counts["a"] += 1 # works without KeyError
Functions

Functions are first-class objects in Python. They can be assigned, passed as arguments, and returned from other functions.

functions.py
Python
1# Basic function
2def greet(name: str) -> str:
3 """Return a greeting. (This is a docstring.)"""
4 return f"Hello, {name}"
5
6# Default arguments (evaluated once at definition!)
7def power(base, exp=2):
8 return base ** exp
9
10# Keyword and positional arguments
11def f(a, b, *args, kw1=None, kw2=None, **kwargs):
12 """a, b: positional required
13 *args: extra positional (tuple)
14 kw1, kw2: keyword-only (must be named)
15 **kwargs: extra keyword (dict)"""
16 pass
17
18# Lambda (anonymous function)
19square = lambda x: x ** 2
20sorted(pairs, key=lambda x: x[1]) # sort by second element
21
22# Type annotations with complex types
23from typing import List, Optional, Dict, Union, Callable
24
25def process(items: List[int], callback: Callable[[int], str]) -> List[str]:
26 return [callback(x) for x in items]
27
28# Nested functions and closures
29def make_multiplier(factor: int):
30 def multiply(x: int) -> int:
31 return x * factor
32 return multiply
33
34double = make_multiplier(2)
35print(double(5)) # → 10
Comprehensions & Generators

Comprehensions provide a concise way to create sequences. They are more readable and faster than manual loops in most cases.

comprehensions.py
Python
1# List comprehension
2squares = [x ** 2 for x in range(10)]
3
4# With condition
5evens = [x for x in range(20) if x % 2 == 0]
6
7# Nested loops
8pairs = [(x, y) for x in [1, 2] for y in [3, 4]]
9# → [(1,3), (1,4), (2,3), (2,4)]
10
11# Set comprehension
12unique = {len(w) for w in ["hi", "hello", "hey", "hi"]}
13# → {2, 3, 5}
14
15# Dict comprehension
16square_map = {x: x ** 2 for x in range(5)}
17# → {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
18
19# Generator expression — lazy, memory-efficient
20gen = (x ** 2 for x in range(10_000_000))
21print(next(gen)) # → 0
22print(next(gen)) # → 1
23
24# Generator function with yield
25def fibonacci(n):
26 a, b = 0, 1
27 for _ in range(n):
28 yield a
29 a, b = b, a + b
30
31list(fibonacci(10)) # → [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
Error Handling

Python uses exceptions for error handling. The try-except-finally pattern gives you control over error recovery.

errors.py
Python
1# Basic try-except
2try:
3 result = 10 / 0
4except ZeroDivisionError:
5 print("can't divide by zero")
6
7# Multiple exception types
8try:
9 value = int(input("enter a number: "))
10 result = 100 / value
11except ValueError:
12 print("that's not a number")
13except ZeroDivisionError:
14 print("can't divide by zero")
15except Exception as e:
16 print(f"unexpected error: {e}")
17
18# Else and Finally
19try:
20 file = open("data.txt")
21 data = file.read()
22except FileNotFoundError:
23 print("file not found")
24else:
25 print(f"read {len(data)} characters") # runs if no exception
26finally:
27 file.close() # always runs — good for cleanup
28
29# Custom exceptions
30class ValidationError(Exception):
31 """Raised when data validation fails."""
32 pass
33
34def validate_age(age: int):
35 if age < 0:
36 raise ValidationError("age cannot be negative")
37
38# Context managers (with statement)
39with open("file.txt", "r") as f: # auto-closes
40 content = f.read()
Object-Oriented Programming

Python supports full OOP with classes, inheritance, polymorphism, and encapsulation. Everything in Python is an object, including classes themselves.

oop.py
Python
1# Class definition
2class Dog:
3 # Class variable (shared by all instances)
4 species = "Canis familiaris"
5
6 # Constructor — called when instance created
7 def __init__(self, name: str, age: int):
8 self.name = name # instance variable
9 self.age = age
10
11 # Instance method
12 def bark(self) -> str:
13 return f"{self.name} says Woof!"
14
15 # String representation (used by print())
16 def __str__(self) -> str:
17 return f"{self.name} ({self.age} years old)"
18
19 # Official representation (used by repr())
20 def __repr__(self) -> str:
21 return f"Dog('{self.name}', {self.age})"
22
23# Creating instances
24rex = Dog("Rex", 3)
25print(rex.bark()) # → Rex says Woof!
26print(rex) # → Rex (3 years old)
27
28# Inheritance
29class Puppy(Dog):
30 def __init__(self, name: str, age: int, toy: str = "ball"):
31 super().__init__(name, age) # call parent constructor
32 self.toy = toy
33
34 # Override parent method
35 def bark(self) -> str:
36 return f"{self.name} yips!"
37
38# Duck typing — "if it walks like a duck..."
39class Cat:
40 def bark(self) -> str:
41 return "Cat goes... bark?"
42
43def make_it_bark(animal):
44 print(animal.bark())
45
46make_it_bark(rex) # → Rex says Woof!
47make_it_bark(Cat()) # → Cat goes... bark?
48
49# Property decorator
50class Temperature:
51 def __init__(self, celsius: float):
52 self._celsius = celsius
53
54 @property
55 def fahrenheit(self) -> float:
56 return self._celsius * 9 / 5 + 32
57
58 @fahrenheit.setter
59 def fahrenheit(self, value: float):
60 self._celsius = (value - 32) * 5 / 9
Modules & Packages

Python's module system lets you organize code into reusable files. Any .py file is a module, and directories with __init__.py become packages.

modules.py
Python
1# Importing modules
2import math
3from pathlib import Path
4from collections import defaultdict, Counter
5from typing import Optional as Opt
6
7# Module structure
8# mypackage/
9# __init__.py # makes it a package
10# module_a.py
11# subpackage/
12# __init__.py
13# module_b.py
14
15# __init__.py can control what gets exported
16# __all__ = ["func1", "ClassA"] # restricts `from pkg import *`
17
18# The if __name__ guard — prevents code from running on import
19def main():
20 print("Running as script")
21
22if __name__ == "__main__":
23 main()
24
25# Standard library highlights everyone should know:
26import os # operating system interface
27import sys # Python interpreter access
28import json # JSON parsing
29import re # regular expressions
30import datetime # date and time handling
31import itertools # iterator tools
32import functools # higher-order functions
33import pathlib # modern filesystem paths
34import collections # specialized containers
35import dataclasses # data classes (3.7+)
Decorators

Decorators are functions that take another function and extend its behavior without modifying it directly. They are a powerful metaprogramming tool.

decorators.py
Python
1# Basic decorator pattern
2from functools import wraps
3
4def timer(func):
5 """Print how long a function takes to run."""
6 @wraps(func) # preserves func's metadata
7 def wrapper(*args, **kwargs):
8 import time
9 start = time.perf_counter()
10 result = func(*args, **kwargs)
11 elapsed = time.perf_counter() - start
12 print(f"{func.__name__} took {elapsed:.4f}s")
13 return result
14 return wrapper
15
16@timer
17def slow_function():
18 sum(range(10_000_000))
19
20slow_function() # → slow_function took 0.2345s
21
22# Decorators with arguments
23def repeat(n: int):
24 def decorator(func):
25 @wraps(func)
26 def wrapper(*args, **kwargs):
27 for _ in range(n):
28 func(*args, **kwargs)
29 return wrapper
30 return decorator
31
32@repeat(3)
33def say_hi():
34 print("hi")
35
36say_hi() # prints "hi" three times
37
38# Built-in decorators
39@staticmethod # method that doesn't receive self
40@classmethod # method that receives cls instead of self
41@property # method accessed like an attribute
42@dataclass # auto-generates __init__, __repr__, etc.
Concurrency & Parallelism

Python offers multiple concurrency models. The Global Interpreter Lock (GIL) affects threading, but asyncio and multiprocessing bypass it for different use cases.

concurrency.py
Python
1# Threading — I/O-bound tasks (GIL-limited)
2import threading
3import time
4
5def worker(name: str, delay: float):
6 time.sleep(delay)
7 print(f"{name} done")
8
9threads = []
10for i in range(3):
11 t = threading.Thread(target=worker, args=(f"T{i}", i))
12 threads.append(t)
13 t.start()
14for t in threads:
15 t.join() # wait for all to finish
16
17# Asyncio — cooperative concurrency (Python 3.5+)
18import asyncio
19
20async def fetch_data(url: str) -> str:
21 await asyncio.sleep(1) # simulate I/O
22 return f"data from {url}"
23
24async def main():
25 tasks = [fetch_data(f"url_{i}") for i in range(5)]
26 results = await asyncio.gather(*tasks)
27 print(results)
28
29asyncio.run(main())
30
31# Multiprocessing — CPU-bound tasks (bypasses GIL)
32from multiprocessing import Pool
33
34def expensive(n: int) -> int:
35 return sum(i * i for i in range(n))
36
37with Pool(processes=4) as pool:
38 results = pool.map(expensive, [10_000, 20_000, 30_000])
39
40# Concurrent futures (high-level API)
41from concurrent.futures import ThreadPoolExecutor
42
43with ThreadPoolExecutor(max_workers=4) as executor:
44 futures = [executor.submit(worker, f"W{i}", i) for i in range(3)]
45 for f in futures:
46 f.result()
Testing

Python has built-in testing frameworks and a rich ecosystem of testing tools.

testing.py
Python
1# unittest — built-in
2import unittest
3
4def add(a: int, b: int) -> int:
5 return a + b
6
7class TestMath(unittest.TestCase):
8 def test_add(self):
9 self.assertEqual(add(2, 3), 5)
10
11 def test_add_negative(self):
12 self.assertEqual(add(-1, 1), 0)
13
14if __name__ == "__main__":
15 unittest.main()
16
17# pytest — third-party, cleaner syntax (pip install pytest)
18# test_math.py:
19# def test_add():
20# assert add(2, 3) == 5
21#
22# def test_add_negative():
23# assert add(-1, 1) == 0
24#
25# Run: pytest test_math.py -v
26
27# Fixtures in pytest
28# @pytest.fixture
29# def db_connection():
30# conn = create_connection()
31# yield conn
32# conn.close()
33#
34# def test_query(db_connection):
35# result = db_connection.query("SELECT 1")
36# assert result == 1
37
38# Mocking
39from unittest.mock import Mock, patch
40
41# Mock an external API call
42def get_user_name(api, user_id):
43 return api.fetch(user_id)["name"]
44
45def test_get_user_name():
46 mock_api = Mock()
47 mock_api.fetch.return_value = {"name": "Alice"}
48 assert get_user_name(mock_api, 1) == "Alice"
File I/O & Context Managers

The with statement (context manager) ensures proper resource cleanup, even if exceptions occur.

file_io.py
Python
1# Reading files
2with open("example.txt", "r") as f:
3 content = f.read() # entire file as string
4 lines = f.readlines() # list of lines
5 for line in f: # lazy iteration (memory efficient)
6 print(line.strip())
7
8# Writing files
9with open("output.txt", "w") as f:
10 f.write("Hello, World!\n")
11 f.writelines(["line1\n", "line2\n"])
12
13# Binary mode
14with open("image.jpg", "rb") as f:
15 data = f.read()
16
17# Pathlib — modern path handling (Python 3.4+)
18from pathlib import Path
19
20p = Path("/tmp/data/file.txt")
21print(p.name) # → file.txt
22print(p.stem) # → file
23print(p.suffix) # → .txt
24print(p.parent) # → /tmp/data
25print(p.exists()) # → True/False
26
27# Read/write with pathlib
28Path("hello.txt").write_text("Hello")
29text = Path("hello.txt").read_text()
30
31# Custom context manager
32class ManagedFile:
33 def __init__(self, name: str):
34 self.name = name
35
36 def __enter__(self):
37 self.file = open(self.name, "w")
38 return self.file
39
40 def __exit__(self, exc_type, exc_val, exc_tb):
41 self.file.close()
42
43# Or use contextlib for simpler cases
44from contextlib import contextmanager
45
46@contextmanager
47def open_file(name: str):
48 f = open(name, "w")
49 try:
50 yield f
51 finally:
52 f.close()
Advanced Topics

These patterns separate beginner Python from professional Python. Mastering them will make your code cleaner, faster, and more Pythonic.

advanced.py
Python
1# Data Classes (Python 3.7+)
2from dataclasses import dataclass, field
3
4@dataclass(order=True)
5class Point:
6 x: float
7 y: float
8 label: str = field(default="", compare=False)
9
10p1 = Point(1.0, 2.0)
11p2 = Point(3.0, 4.0)
12print(p1) # → Point(x=1.0, y=2.0, label='')
13print(p1 < p2) # → True (compares x first)
14
15# Enums
16from enum import Enum, auto
17
18class Color(Enum):
19 RED = auto()
20 GREEN = auto()
21 BLUE = auto()
22
23print(Color.RED.name) # → RED
24print(Color.RED.value) # → 1
25
26# TypeAlias (Python 3.10+)
27from typing import TypeAlias
28
29Vector: TypeAlias = list[float]
30Matrix: TypeAlias = list[Vector]
31
32# Structural pattern matching (3.10+) — advanced
33def process_command(cmd: str) -> str:
34 match cmd.split():
35 case ["quit"]:
36 return "Goodbye"
37 case ["load", filename]:
38 return f"Loading {filename}"
39 case ["save", *rest] if rest:
40 return f"Saving {rest[0]}"
41 case _:
42 return "Unknown command"
43
44# Walrus operator (3.8+) — assignment expression
45if (n := len(items)) > 10:
46 print(f"Large list: {n} items")
47
48# ZoneInfo — timezone support (3.9+)
49from zoneinfo import ZoneInfo
50from datetime import datetime
51nyc = datetime.now(ZoneInfo("America/New_York"))
52
53# Generic types (3.12+) — concise generics
54def first[T](items: list[T]) -> T | None:
55 return items[0] if items else None
Ecosystem & Tooling

Python's power comes from its ecosystem. These tools and libraries are essential for modern Python development.

CategoryTools / LibrariesPurpose
Package Mgmtpip, uv, poetry, ryeInstall and manage dependencies
Environmentvenv, conda, pyenvIsolate project dependencies
Lintingruff, flake8, pylintCatch errors and enforce style
Formattingruff format, blackAuto-format code
Type Checkingmypy, pyright, pyreStatic type analysis
Testingpytest, unittest, toxTest runner and automation
WebFastAPI, Django, FlaskWeb frameworks
DataNumPy, Pandas, PolarsData manipulation
ML/AIPyTorch, scikit-learn, transformersMachine learning

best practice

For new projects: use uv for package management, ruff for linting + formatting, mypy for type checking, and pytest for testing. This combo is modern, fast, and widely adopted.
The Zen of Python

Tim Peters' Zen of Python (PEP 20) captures Python's design philosophy. Type import this in any Python interpreter to see it.

untitled.python
Python
1Beautiful is better than ugly.
2Explicit is better than implicit.
3Simple is better than complex.
4Complex is better than complicated.
5Flat is better than nested.
6Sparse is better than dense.
7Readability counts.
8Special cases aren't special enough to break the rules.
9Although practicality beats purity.
10Errors should never pass silently.
11Unless explicitly silenced.
12In the face of ambiguity, refuse the temptation to guess.
13There should be one — and preferably only one — obvious way to do it.
14Although that way may not be obvious at first unless you're Dutch.
15Now is better than never.
16Although never is often better than *right* now.
17If the implementation is hard to explain, it's a bad idea.
18If the implementation is easy to explain, it may be a good idea.
19Namespaces are one honking great idea — let's do more of those!
$Blueprint — Engineering Documentation·Section ID: PYTHON-01·Revision: 1.0

Community

Get help on Slack, Discord or VIP

Stuck on a guide? Join the community and ask.