Python 3
Table of Contents
- 1. Language
- 2. 2026 Review
- 3. Resources
- 4. Related
1. Language
1.1. Control Flow
# Conditionals
x = 10
if x > 0:
print("positive")
elif x < 0:
print("negative")
else:
print("zero")
# Match statement (Python 3.10+)
match command:
case "start":
start_server()
case "stop":
stop_server()
case _:
print("Unknown command")
# Loops
for i in range(5):
print(i)
while condition:
do_something()
if should_stop:
break
1.2. Data Structures
# Lists
numbers = [1, 2, 3, 4, 5]
numbers.append(6)
squares = [x**2 for x in numbers]
# Dictionaries
person = {"name": "Alice", "age": 30}
person["email"] = "alice@example.com"
names = {p["id"]: p["name"] for p in people}
# Sets
unique = {1, 2, 3, 2, 1} # {1, 2, 3}
a = {1, 2, 3}
b = {2, 3, 4}
a & b # intersection: {2, 3}
a | b # union: {1, 2, 3, 4}
# Tuples (immutable)
point = (3, 4)
x, y = point # unpacking
1.3. Error Handling
try:
result = risky_operation()
except ValueError as e:
print(f"Value error: {e}")
except (TypeError, KeyError) as e:
print(f"Error: {e}")
else:
print("Success!")
finally:
cleanup()
# Context managers
with open("file.txt") as f:
content = f.read()
# Custom exceptions
class ValidationError(Exception):
pass
1.4. Classes
from dataclasses import dataclass
from typing import Optional
# Traditional class
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def greet(self) -> str:
return f"Hello, I'm {self.name}"
# Dataclass (Python 3.7+)
@dataclass
class User:
name: str
email: str
age: Optional[int] = None
def is_adult(self) -> bool:
return self.age is not None and self.age >= 18
1.5. Packaging
# pyproject.toml (modern Python packaging)
[project]
name = "mypackage"
version = "0.1.0"
dependencies = [
"requests>=2.28",
"pydantic>=2.0",
]
[project.optional-dependencies]
dev = ["pytest", "ruff", "mypy"]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
1.6. Development
1.6.1. Environment
# Using uv (fast Python package installer)
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
# Or with pip
python -m venv .venv
pip install -e ".[dev]"
1.6.2. Testing
# pytest example
import pytest
def test_addition():
assert 1 + 1 == 2
@pytest.fixture
def sample_data():
return {"key": "value"}
def test_with_fixture(sample_data):
assert sample_data["key"] == "value"
2. 2026 Review
2.1. Python 3.13 (October 2024): Free-Threading and JIT
~PYTHON_GIL=0~ does not enable free-threading "at runtime without recompiling." Per the CPython docs, ~PYTHON_GIL~ only has an effect on a build already compiled with ~--disable-gil~ (the ~python3.13t~ binary); on a standard build it does nothing. Within a free-threaded build, ~PYTHON_GIL=1~ forces the GIL back on and ~PYTHON_GIL=0~ forces it off -- the env var toggles the GIL inside a free-threaded build, it does not substitute for the build itself. The JIT claims in this section (off by default, ~PYTHON_JIT=1~ to enable, built on the 3.11 specializing adaptive interpreter) check out against PEP 744 and the 3.13 release notes.
Python 3.13 shipped two headline features, both experimental but production-relevant:
2.1.1. Free-Threaded Mode (PEP 703)
CPython 3.13 introduced a build option (--disable-gil) that removes the Global Interpreter Lock. Python threads can now run truly in parallel on multi-core hardware. Getting free-threading requires that build (or the prebuilt python3.13t binary) – the PYTHON_GIL environment variable then toggles the GIL within it (PYTHON_GIL=0 forces it off, PYTHON_GIL=1 forces it back on) but has no effect on a standard build.
"""
Demonstrate free-threaded Python 3.13+ with concurrent CPU work.
Run with: PYTHON_GIL=0 python examples/free_threading.py
Requires: Python 3.13+ built with --disable-gil (or 3.13t build)
"""
import sys
import threading
import time
def cpu_bound_task(n: int, results: list[int], index: int) -> None:
"""Compute sum of squares -- pure CPU work."""
total = sum(i * i for i in range(n))
results[index] = total
def run_threaded(num_threads: int = 4, work_size: int = 1_000_000) -> float:
results: list[int] = [0] * num_threads
threads = [
threading.Thread(target=cpu_bound_task, args=(work_size, results, i))
for i in range(num_threads)
]
start = time.perf_counter()
for t in threads:
t.start()
for t in threads:
t.join()
elapsed = time.perf_counter() - start
return elapsed
if __name__ == "__main__":
gil_status = "disabled" if not sys._is_gil_enabled() else "enabled" # type: ignore[attr-defined]
print(f"GIL: {gil_status}")
elapsed = run_threaded()
print(f"4 threads, 1M work each: {elapsed:.3f}s")
2.1.2. JIT Compiler (PEP 744)
Python 3.13 also shipped a copy-and-patch JIT compiler. It is off by default (PYTHON_JIT=1 to enable) and targets hot inner loops. The specialising adaptive interpreter introduced in 3.11 underpins it.
2.2. Python 3.14 (October 2025): Template Strings and Deferred Annotations
2.2.1. Template Strings (PEP 750)
PEP 750 extends f-string syntax with t"..." template literals. Unlike f-strings, template strings return a Template object whose interpolated parts can be inspected before rendering. This makes them safe for SQL, HTML, and shell escaping.
"""
Template strings (PEP 750) -- Python 3.14+.
t-strings return a Template object; the caller controls rendering,
enabling safe interpolation into SQL, HTML, or shell commands.
"""
import html as html_mod
# Standard library html module gains t-string support in 3.14
# This example shows the core pattern without stdlib t-string types.
def render_html(template) -> str:
"""Escape interpolated values for safe HTML output."""
parts: list[str] = []
for item in template:
if isinstance(item, str):
parts.append(item)
else:
# item.value is the runtime expression result
parts.append(html_mod.escape(str(item.value)))
return "".join(parts)
user_input = "<script>alert('xss')</script>"
# In Python 3.14: template = t"<p>Hello, {user_input}!</p>"
# safe_html = render_html(template)
# => "<p>Hello, <script>alert('xss')</script>!</p>"
print("Template strings enable safe interpolation -- escaping is caller-controlled.")
2.2.2. Deferred Evaluation of Annotations (PEP 649)
PEP 649 replaces PEP 563 (from __future__ import annotations). Annotations are stored as lazy descriptors evaluated only when accessed via __annotations__. This eliminates forward-reference errors without the string-quoting workaround and is faster than eager evaluation.
"""
Deferred annotation evaluation (PEP 649) -- Python 3.14+.
Annotations are not evaluated at class/function definition time;
they are computed lazily. Forward references work without quotes.
"""
from __future__ import annotations # backport shim; native in 3.14
from dataclasses import dataclass
@dataclass
class Node:
value: int
# Forward reference -- works without quotes under PEP 649
children: list[Node]
def depth(self) -> int:
if not self.children:
return 0
return 1 + max(child.depth() for child in self.children)
root = Node(value=1, children=[
Node(value=2, children=[]),
Node(value=3, children=[Node(value=4, children=[])]),
])
print(f"Tree depth: {root.depth()}") # => 2
2.3. Python 3.15 Alpha (2026 Preview)
PEP 768 and PEP 779 were both mischaracterized. PEP 768 is "Safe external debugger interface for CPython" (a zero-overhead mechanism for debuggers/profilers to attach to a running process at safe points), not comprehension-scoping cleanup. PEP 779 is "Criteria for supported status for free-threaded Python" (performance/memory budgets and API-stability requirements for promoting the free-threaded build out of experimental status), not an ~asyncio.TaskGroup~ change.
Python 3.15 entered alpha in early 2026. Key proposals under discussion:
- PEP 768 – Safe external debugger interface for CPython: lets a debugger attach to a running process and execute code at defined safe points, without unsafe code injection
- PEP 779 – Criteria for promoting the free-threaded build from experimental to officially supported status (performance and memory budgets, API stability)
- Immortal objects (PEP 683 follow-on) – extended to more built-in types, reducing reference-count churn
sys.monitoringstabilisation – the low-overhead tracing API (3.12) reaches stable ABI in 3.15
2.4. Typing Ecosystem Matured
The typing story that began with PEP 484 (3.5, 2015) reached a stable, practical equilibrium in 2025–2026.
| Feature | PEP | Since | Status |
|---|---|---|---|
TypedDict |
589 | 3.8 | Stable; NotRequired, ReadOnly added |
ParamSpec |
612 | 3.10 | Widely adopted for decorator typing |
TypeVarTuple |
646 | 3.11 | Variadic generics for *args typing |
TypeVar defaults |
696 | 3.13 | Simplifies generic class defaults |
@override |
698 | 3.12 | Explicit subclass method override marker |
type statement |
695 | 3.12 | type Vector = list[float] replaces TypeAlias |
Static analysis is now split between two tools: mypy (pioneer, broad ecosystem support) and pyright (faster, strict, powers Pylance in VS Code).
2.5. Tooling: uv and ruff Replace the Stack
Astral shipped uv (Rust-based Python installer and resolver) and ruff (Rust-based linter and formatter) in 2023–2024. By 2026 they have effectively replaced the previous standard stack:
| Old tool | Replacement | Speed gain |
|---|---|---|
pip + pip-tools |
uv pip / uv sync |
10--100x |
poetry |
uv (project management) |
10x |
virtualenv |
uv venv |
80x |
flake8 |
ruff check |
100x |
black |
ruff format |
30x |
isort |
ruff check --select I |
included |
uv reads the same pyproject.toml as poetry; migration is largely mechanical. The lockfile (uv.lock) is cross-platform and reproducible.
2.6. Python in the Agent Era
Python became the default language for AI agent development in 2024–2026, driven by three converging forces: the Anthropic and OpenAI SDKs shipping Pythonic APIs first, the MCP (Model Context Protocol) SDK targeting Python as its reference implementation, and the pydantic library becoming the canonical interface for structured LLM output.
2.6.1. MCP SDK
The MCP Python SDK provides the reference server and client implementations. An MCP server exposes tools, resources, and prompts over a JSON-RPC transport:
from mcp.server import FastMCP
mcp = FastMCP("my-tool-server")
@mcp.tool()
def search(query: str, limit: int = 10) -> list[dict]:
"""Full-text search over the knowledge base."""
return perform_search(query, limit)
if __name__ == "__main__":
mcp.run() # stdio transport by default
2.6.2. Structured Output with Pydantic
Both the Anthropic and OpenAI SDKs accept Pydantic models directly for structured output:
from anthropic import Anthropic
from pydantic import BaseModel
class CodeReview(BaseModel):
summary: str
issues: list[str]
severity: str # "low" | "medium" | "high"
approved: bool
client = Anthropic()
response = client.messages.create(
model="claude-opus-4-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Review this diff: ..."}],
)
# Structured parsing with tool_use or beta structured output
2.6.3. Key Libraries (2026)
| Library | Purpose | Notes |
|---|---|---|
anthropic |
Claude API client | Streaming, tool_use, vision |
openai |
OpenAI API client | Compatible with many providers |
mcp |
MCP server/client | FastMCP high-level API |
pydantic v2 |
Validation + serialization | Rust core, 5–50x faster than v1 |
httpx |
Async HTTP | Replaces requests in async contexts |
rich |
Terminal output | Progress, tables, syntax highlight |
typer |
CLI framework | Built on click + type hints |
3. Resources
4. Related
- Advanced Python Exercises – exercises
- Python Standard Library Highlights – the standard library
- Modern Python Machine Learning Ecosystem Guide (2024-2025) – the ML ecosystem
- Web, API, Python, and AWS Development Reference – Python on AWS