Skip to content
Volume P · The TensorCore.si Library

Python

The default language of artificial intelligence.

Python reads almost like English, runs everywhere and has the largest collection of AI and data libraries of any language. PyTorch, TensorFlow, JAX and Hugging Face all put Python first.

Prologue

Why learn Python?

Python is the first language most people learn and the last one many AI researchers ever need. Its simple syntax lets you think about the problem instead of the language, and its libraries do the heavy, fast work in C, C++ and CUDA underneath.

  • AI runs on it: almost every major model, from GPT-style transformers to image generators, is trained and served with Python code.
  • Huge ecosystem: more than 500,000 packages on PyPI for data, web, science and automation.
  • Fast to write: a few lines of Python often replace dozens in C++ or Java.
  • Glue language: it calls fast native code, so you get speed where it matters without writing it yourself.
Prologue

Set up and run your first program

Install Python 3.10 or newer from python.org (Windows and macOS) or your package manager (Linux). Then create a project folder with its own virtual environment so its packages stay separate.

terminal
python3 --version
mkdir my-ai-project && cd my-ai-project
python3 -m venv .venv
source .venv/bin/activate      # Windows: .venv\Scripts\activate
pip install numpy
hello.py
print("Hello, AI!")
name = input("What is your name? ")
print(f"Welcome to Python, {name}.")
TipRun it with python hello.py. Type python alone to open the interactive shell (REPL), which is perfect for trying ideas line by line.
Chapter 1

Variables and data types

A variable is a name that points to a value. You do not declare types: Python works them out at run time. The core types are whole numbers (int), decimals (float), text (str), true/false (bool) and "nothing" (None).

Type hints such as age: int are optional notes for you, your editor and tools like mypy. Python does not enforce them when the code runs.

variables.py
age = 21                  # int
price = 19.99             # float
name = "Ada"              # str
is_student = True         # bool
result = None             # no value yet

score: float = 97.5       # optional type hint
print(type(price))        # <class 'float'>
print(f"{name} is {age} years old")   # f-string formatting
print(int("42") + 1)      # convert text to a number: 43
Chapter 2

Making decisions and repeating

Python uses indentation (four spaces) instead of braces to group code. if, elif and else choose a path; for loops over any sequence; while repeats until a condition is false.

Python 3.10 added match, a powerful way to compare a value against several patterns.

control.py
temperature = 31
if temperature > 30:
    print("Hot")
elif temperature > 15:
    print("Mild")
else:
    print("Cold")

for i in range(3):          # 0, 1, 2
    print("step", i)

count = 3
while count > 0:
    count -= 1

command = "train"
match command:
    case "train":
        print("Training the model")
    case "test" | "eval":
        print("Evaluating")
    case _:
        print("Unknown command")
Chapter 3

Functions

A function packages code you want to reuse. Define it with def, give parameters default values if you like, and return a result. *args collects extra positional arguments and **kwargs collects named ones.

Small one-line functions can be written as a lambda, which is handy for sorting and filtering.

functions.py
def accuracy(correct: int, total: int = 100) -> float:
    """Return accuracy as a percentage."""
    return correct / total * 100

print(accuracy(87))            # 87.0
print(accuracy(45, total=50))  # 90.0

def log(*messages, **options):
    sep = options.get("sep", " ")
    print(sep.join(messages))

log("loss", "falling", sep=" -> ")

models = [("llama", 8), ("qwen", 32), ("phi", 4)]
models.sort(key=lambda m: m[1])   # sort by size
print(models)
Chapter 4

Lists, dictionaries, sets and tuples

A list is an ordered, changeable sequence. A tuple is like a list that cannot change. A dictionary maps keys to values, and a set stores unique items.

Comprehensions build new collections in one readable line, and they are one of the most loved features of Python.

collections.py
gpus = ["RTX 4090", "H100", "MI300X"]
gpus.append("B200")
print(gpus[0], gpus[-1], len(gpus))

specs = {"H100": 80, "B200": 180}   # GB of memory
specs["MI300X"] = 192
for name, gb in specs.items():
    print(name, gb)

point = (3, 4)                       # tuple
unique = {"cat", "dog", "cat"}       # {'cat', 'dog'}

squares = [x * x for x in range(6)]                # [0, 1, 4, 9, 16, 25]
big = {k: v for k, v in specs.items() if v > 100}  # dict comprehension
print(squares, big)
Chapter 5

Classes and objects

A class is a blueprint for objects that bundle data (attributes) and behaviour (methods). __init__ runs when an object is created, and self refers to that object.

For classes that mainly hold data, @dataclass writes the boring parts for you. Inheritance lets one class extend another.

classes.py
from dataclasses import dataclass

class Model:
    def __init__(self, name: str, params_b: float):
        self.name = name
        self.params_b = params_b

    def memory_gb(self, bytes_per_param: float = 2) -> float:
        return self.params_b * bytes_per_param

class ChatModel(Model):                # inheritance
    def reply(self, text: str) -> str:
        return f"{self.name} says: you wrote '{text}'"

@dataclass
class GPU:
    name: str
    vram_gb: int

llama = ChatModel("Llama 3.1 8B", 8)
print(llama.memory_gb())       # 16.0 GB in FP16
print(llama.reply("hi"))
print(GPU("RTX 5090", 32))     # GPU(name='RTX 5090', vram_gb=32)
Chapter 6

Handling errors

When something goes wrong, Python raises an exception. Catch the ones you expect with try and except, run clean-up code in finally, and raise your own errors with raise.

Catch specific exceptions such as ValueError rather than everything, so real bugs are not hidden.

errors.py
class OutOfMemory(Exception):
    pass

def load(model_gb: float, vram_gb: float):
    if model_gb > vram_gb:
        raise OutOfMemory(f"needs {model_gb} GB, only {vram_gb} GB free")
    return "loaded"

try:
    print(load(140, 24))
except OutOfMemory as e:
    print("Could not load:", e)
except ValueError:
    print("Bad number")
finally:
    print("done")
Chapter 7

Modules, packages and pip

Every .py file is a module you can import. A folder of modules is a package. Python ships with a large standard library, and pip installs anything else from PyPI.

Keep each project in its own virtual environment and record its dependencies in requirements.txt or pyproject.toml. Modern tools like uv make this very fast.

main.py
# utils.py
def to_gb(n_bytes: int) -> float:
    return n_bytes / 1e9

# main.py
import math
from pathlib import Path
from utils import to_gb

print(math.sqrt(16), to_gb(8_000_000_000))

if __name__ == "__main__":   # runs only when executed directly
    print("running main.py")
Key ideapip install torch installs PyTorch. pip freeze > requirements.txt saves your exact versions so others can recreate your environment.
Chapter 8

Files and JSON

Use with open(...) so files close automatically, even if an error happens. pathlib handles paths on every operating system, and the json module reads and writes the format most AI APIs use.

files.py
import json
from pathlib import Path

results = {"model": "llama3.1", "accuracy": 0.91}
Path("results.json").write_text(json.dumps(results, indent=2))

data = json.loads(Path("results.json").read_text())
print(data["accuracy"])

with open("log.txt", "a", encoding="utf-8") as f:
    f.write("epoch 1 done\n")

for line in open("log.txt", encoding="utf-8"):
    print(line.strip())
Chapter 9

Concurrency and async

Waiting on networks (like calling an AI API) is best handled with asyncio: while one request waits, others run. For CPU-heavy work, use multiprocessing or a library that releases the global interpreter lock, such as NumPy or PyTorch.

Since Python 3.13 there is an optional "free-threaded" build without the lock, still experimental.

concurrency.py
import asyncio

async def ask(model: str, delay: float) -> str:
    await asyncio.sleep(delay)          # stands in for a network call
    return f"{model} answered"

async def main():
    answers = await asyncio.gather(
        ask("model-a", 1.0),
        ask("model-b", 1.0),
        ask("model-c", 1.0),
    )
    print(answers)                       # takes about 1 s, not 3 s

asyncio.run(main())
Chapter 10

Testing and tools

Professional Python uses a few standard tools: pytest for tests, ruff for linting and formatting, and mypy or pyright for checking type hints. Jupyter notebooks are the favourite for data exploration.

test_utils.py
# test_utils.py   (run with: pytest)
from utils import to_gb

def test_to_gb():
    assert to_gb(2_000_000_000) == 2.0

def test_zero():
    assert to_gb(0) == 0
Part II · AI with Python

Machine learning with Python

Python is where models are designed, trained and evaluated. You write readable Python, and libraries hand the maths to optimised C++ and CUDA kernels running on the GPU's Tensor Cores.

A typical path: NumPy and pandas for data, scikit-learn for classic machine learning, then PyTorch for deep learning and Hugging Face for ready-made models.

The essential libraries

NumPyFast n-dimensional arrays: the foundation of scientific Python.
pandas / PolarsTables of data: load, clean, join and summarise.
scikit-learnClassic ML: regression, trees, clustering, evaluation.
PyTorchThe most used deep-learning framework in research and industry.
JAXNumPy-style code compiled for GPUs and TPUs.
Hugging Face TransformersThousands of pretrained language, vision and audio models.
vLLMHigh-throughput serving of large language models.
LangChain / LlamaIndexBuilding blocks for chatbots, retrieval and agents.

Train your first neural network in PyTorch

This model learns the line y = 3x + 0.5 from noisy data. The same four steps (predict, measure loss, backpropagate, update) train every neural network, up to the largest language models.

train.py
import torch
from torch import nn

X = torch.linspace(-1, 1, 100).unsqueeze(1)
y = 3 * X + 0.5 + 0.1 * torch.randn_like(X)    # noisy data

model = nn.Linear(1, 1)
opt = torch.optim.SGD(model.parameters(), lr=0.1)
loss_fn = nn.MSELoss()

for step in range(200):
    opt.zero_grad()
    loss = loss_fn(model(X), y)   # 1. predict and 2. measure error
    loss.backward()               # 3. compute gradients
    opt.step()                    # 4. update weights

print(model.weight.item(), model.bias.item())  # close to 3.0 and 0.5

To use a GPU, move the model and data with .to("cuda"). See how AI works for what happens inside, and the VRAM calculator to check which models fit your card.

Part III · Build an AI agent

Build an AI agent in Python

An agent is a language model in a loop that can use tools. You describe your tools in JSON, the model decides when to call one, your code runs it and sends back the result, and the model continues until it can answer.

This complete agent uses only the requests package and a local model, so it costs nothing to run.

Your goal→Model plans→Tool call→Your code runs the tool→Result back to model→Final answer
  1. Describe each tool (name, purpose, parameters) as JSON.
  2. Send the conversation and the tool list to the model.
  3. If the reply contains tool_calls, run each tool and append the result as a tool message.
  4. Repeat until the model replies with plain text.
agent.py
import json
import requests   # pip install requests

URL = "http://localhost:11434/v1/chat/completions"
MODEL = "llama3.1"

def get_weather(city: str) -> str:
    return f"It is 24°C and sunny in {city}."   # swap in a real weather API

TOOLS = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get the current weather for a city",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]

messages = [{"role": "user", "content": "Should I take an umbrella in Lahore today?"}]

while True:
    reply = requests.post(URL, json={"model": MODEL, "messages": messages, "tools": TOOLS}).json()
    msg = reply["choices"][0]["message"]
    messages.append(msg)
    if not msg.get("tool_calls"):          # no tool needed: final answer
        print(msg["content"])
        break
    for call in msg["tool_calls"]:
        args = json.loads(call["function"]["arguments"])
        result = get_weather(**args)
        messages.append({"role": "tool", "tool_call_id": call["id"], "content": result})

Add more tools (search, a calculator, your database) by writing a function and adding its description to TOOLS. Frameworks such as LangGraph, the OpenAI Agents SDK and Pydantic AI add memory, retries and multi-agent workflows on top of this same loop.

Run it locallyInstall Ollama, run ollama pull llama3.1, and keep it running. The code talks to its OpenAI-compatible API at http://localhost:11434/v1, so the same program also works with vLLM, llama.cpp server or a cloud API by changing the URL, model name and adding an API key.
Part IV · Your path

Python learning roadmap

Tick items off as you go. Your progress is saved in this browser.

Beginner

Intermediate

Advanced

Part IV · Your path

Projects to build

Expense trackerRead a CSV of spending, summarise it by category and plot a chart.
Web scraperCollect headlines from a news site and save them to JSON.
Image classifierFine-tune a small vision model on your own photos with PyTorch.
Chat with your PDFsEmbed documents, retrieve the right passages and answer with a local LLM.
Research agentAn agent that searches, reads pages and writes a cited summary.
GPU benchmarkTime matrix multiplication on CPU vs GPU and chart the speed-up.
Reference

Python cheat sheet

TaskSyntax
Printprint("Hi", name)
Format textf"{value:.2f}"
Listxs = [1, 2, 3]; xs.append(4)
Dictionaryd = {"a": 1}; d.get("b", 0)
Loop with indexfor i, x in enumerate(xs):
Comprehension[x * 2 for x in xs if x > 1]
Functiondef f(a, b=2): return a + b
Lambdalambda x: x * 2
Classclass A: def __init__(self): ...
Exceptiontry: ... except ValueError as e: ...
Read filePath("f.txt").read_text()
Install packagepip install numpy
Reference

Common mistakes

  • Mixing tabs and spaces: use four spaces everywhere; most editors do this for you.
  • Mutable default arguments: def f(x=[]) shares one list between calls. Use x=None and create the list inside.
  • Forgetting the virtual environment: installing packages globally leads to version clashes between projects.
  • Expecting speed from pure Python loops: use NumPy or PyTorch operations on whole arrays instead of looping element by element.
  • Comparing with is instead of ==: is checks identity, not equality. Use it only for None.
Reference

Where to go next

On TensorCore.si: How AI works, What is CUDA?, LLM inference simulator, Best GPU for local LLMs.

Keyboard: [ previous language · ] next language · / search the library