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.
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.
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.
python3 --version
mkdir my-ai-project && cd my-ai-project
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install numpyprint("Hello, AI!")
name = input("What is your name? ")
print(f"Welcome to Python, {name}.")python hello.py. Type python alone to open the interactive shell (REPL), which is perfect for trying ideas line by line.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.
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: 43Making 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.
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")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.
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)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.
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)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.
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)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.
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")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.
# 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")pip install torch installs PyTorch. pip freeze > requirements.txt saves your exact versions so others can recreate your environment.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.
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())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.
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())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 (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) == 0Machine 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
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.
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.5To 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.
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.
- Describe each tool (name, purpose, parameters) as JSON.
- Send the conversation and the tool list to the model.
- If the reply contains
tool_calls, run each tool and append the result as atoolmessage. - Repeat until the model replies with plain text.
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.
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.Python learning roadmap
Tick items off as you go. Your progress is saved in this browser.
Beginner
Intermediate
Advanced
Projects to build
Python cheat sheet
| Task | Syntax |
|---|---|
print("Hi", name) | |
| Format text | f"{value:.2f}" |
| List | xs = [1, 2, 3]; xs.append(4) |
| Dictionary | d = {"a": 1}; d.get("b", 0) |
| Loop with index | for i, x in enumerate(xs): |
| Comprehension | [x * 2 for x in xs if x > 1] |
| Function | def f(a, b=2): return a + b |
| Lambda | lambda x: x * 2 |
| Class | class A:
def __init__(self): ... |
| Exception | try: ...
except ValueError as e: ... |
| Read file | Path("f.txt").read_text() |
| Install package | pip install numpy |
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. Usex=Noneand 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
isinstead of==:ischecks identity, not equality. Use it only forNone.
Where to go next
- The official Python tutorial — the best free starting point
- PEP 8 style guide
- PyTorch tutorials
- Hugging Face course — free courses on LLMs and agents
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