Cloud Explainers

The Python Roadmap for Beginners

Python8 min videoPublished Nov 13, 2026

Learning Python isn't hard because of the syntax: beginners get stuck on python: command not found, a TypeError about an int and a str, and pip refusing to install anything. This roadmap puts the stages in the order that clears each wall before you hit it: run Python, the basics, errors and files, packages in a virtual environment, one real project, and tests with Git. We follow one small bakery program from the first command to a tested spreadsheet report, all run for real on Ubuntu.

The Python Roadmap for Beginners, the full picture: diagram of Run Python, The basics, Errors and files, Packages, One project, Tests and Git, The car park: Python on its own, The streets: real programs
The whole picture, from the video's recap.

Where beginners get stuck

You've decided to learn Python, and you expect the hard part to be the syntax. It usually isn't. Here are the first three walls, for real. You type python: command not found. Your first program stops with a type error, about an int and a str. And pip refuses to install anything: externally managed environment. None of them is about syntax, and each one belongs to a stage you can learn before you hit it.

Ana's laptop
pythonCommand 'python' not found, did you mean:
  command 'python3' from deb python3
  command 'python' from deb python-is-python3
Ana's laptop
python3 totals.pyTraceback (most recent call last):
  File "/home/ana/orders/totals.py", line 17, in <module>
    for item, qty in totals(read_orders("orders.csv")).items():
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ana/orders/totals.py", line 13, in totals
    sold[item] = sold.get(item, 0) + row["qty"]
                 ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Ana's laptop
pip install openpyxlerror: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
    sure you have python3-full installed.

    If you wish to install a non-Debian packaged Python application,
    it may be easiest to use pipx install xyz, which will manage a
    virtual environment for you. Make sure you have pipx installed.

    See /usr/share/doc/python3.12/README.venv for more information.

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.

Here's an order that works, in six stages. Like learning to drive: an empty car park first, Python on its own, then the streets: other people's code, a real project, and keeping it working. We'll follow one program, at a neighbourhood bakery. One number in it will be wrong, and its spreadsheet won't show it.

Stage 1: run Python

Stage one sounds too simple to need a stage. On Ubuntu, a popular Linux, the command is python3; on Windows it's py. Plain python gets you this, and the helper even suggests the fix. Python 3.12, already installed. With nothing after it, python3 opens the REPL: three arrows, waiting for one line, and answering at once. It's where you try things out.

Ana's laptop
python3 --versionPython 3.12.3
Ana's laptop
python3Python 3.12.3 (main, Aug 31 2026, 10:18:26) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
Ana's laptop
>>> 2 + 35
Ana's laptop
>>> print("Good morning")Good morning

A script is the same lines, saved from any text editor in a file ending in.py; cat prints a file to the screen. You run it with python3 and the file's name. That's the whole of stage one: the REPL to try a line, a file to keep it.

Ana's laptop
cat hello.pyprint("Good morning from the bakery")
Ana's laptop
python3 hello.pyGood morning from the bakery

Stage 2: the basics

Stage two is four ideas: values and their types, lists and dictionaries, if and for, and functions. Here's the one that trips people first.

Stage 2: four ideas: cards of Values and types, Lists and dictionaries, If and for, Functions, Episode: Every Python Concept for Beginners in 10 Minutes

Every value has a type. A number read from a file arrives as text, a string. Here, the quantity is the string three. Guess first: what does times two give? Thirty-three. Times two repeats the text. int turns it into a whole number, and now it's six.

Ana's laptop
python3Python 3.12.3 (main, Aug 31 2026, 10:18:26) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
Ana's laptop
>>> qty = "3"
Ana's laptop
>>> type(qty)<class 'str'>
Ana's laptop
>>> qty * 2'33'
Ana's laptop
>>> int(qty) * 26

Two more you'll use every day. A list keeps things in order, and len counts them. A dictionary looks a value up by its name: the croissant's price.

Ana's laptop
>>> items = ["rye loaf", "croissant", "sourdough"]
Ana's laptop
>>> len(items)3
Ana's laptop
>>> prices = {"rye loaf": 4.50, "croissant": 2.20}
Ana's laptop
>>> prices["croissant"]2.2

Loops and functions put them together. A function takes the orders, and a loop goes through them one row at a time, adding each row's quantity to its item in a dictionary. At the end, it hands the dictionary back: each item and how many sold.

A loop that fills a dictionary: diagram of orders.csv, totals(orders), for row in orders, {"rye loaf": 5,...}

Stage 3: errors and files

Why give errors a stage of their own? Because you'll read more of them than anything else. Ana's first real program reads the bakery's orders from a CSV file, plain text with commas between columns, and adds up how many of each item sold. She runs it, and gets this: a traceback.

Ana's laptop
cat orders.csvitem,qty,price
rye loaf,3,4.50
croissant,12,2.20
sourdough,4,5.80
rye loaf,2,4.50
croissant,6,2.20
cinnamon bun,3,1.10
Ana's laptop
cat totals.pyimport csv


def read_orders(path):
    with open(path) as f:
        return list(csv.DictReader(f))


def totals(orders):
    sold = {}
    for row in orders:
        item = row["item"]
        sold[item] = sold.get(item, 0) + row["qty"]
    return sold


for item, qty in totals(read_orders("orders.csv")).items():
    print(f"{item}: {qty}")

Read a traceback from the bottom. The last line says what went wrong: a type error. Plus, between an int and a str. The line above says where: line 13, adding the quantity. It's the same string as in stage two: the file gave her text.

Read the last line first: diagram of TypeError... +: 'int' and 'str', TypeError = What kind, + = The operation, 'int' and 'str' = The two types, The line above: line 13, sold.get(item, 0) + row["qty"]

She wraps the quantity in int; grep, which prints the lines that contain a word, shows the change. And the totals come out: five rye loaves, eighteen croissants. The other error you'll meet in this stage: a file that isn't where Python looks. We moved the CSV away and ran it again. Same traceback, read the same way: the last line names the missing file.

Ana's laptop
grep qty totals.py        sold[item] = sold.get(item, 0) + int(row["qty"])
for item, qty in totals(read_orders("orders.csv")).items():
    print(f"{item}: {qty}")
Ana's laptop
python3 totals.pyrye loaf: 5
croissant: 18
sourdough: 4
cinnamon bun: 3
Ana's laptop
python3 totals.pyTraceback (most recent call last):
  File "/home/ana/orders/totals.py", line 17, in <module>
    for item, qty in totals(read_orders("orders.csv")).items():
                            ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/ana/orders/totals.py", line 5, in read_orders
    with open(path) as f:
         ^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: 'orders.csv'

Stage 4: packages

Now the streets, where other people's code comes in. The owner wants a spreadsheet, and Python can't write Excel files on its own. A package called openpyxl can: someone else's code, that you install. head shows the top of her new version, and import loads that package's tools into her program. Python can't find it yet. And pip, the installer, refuses: externally managed environment.

Ana's laptop
head -n 3 totals.pyimport csv

from openpyxl import Workbook
Ana's laptop
python3 totals.pyTraceback (most recent call last):
  File "/home/ana/orders/totals.py", line 3, in <module>
    from openpyxl import Workbook
ModuleNotFoundError: No module named 'openpyxl'

Why? Ubuntu runs parts of itself on that Python, so it's protected. The answer is a virtual environment: a folder with the project's own Python and its own packages. Each project gets one, and nothing it installs can reach the system's.

One environment per project: diagram of Ubuntu's Python, .venv, openpyxl, pytest, Next project, Ana's bakery project

python3 -m venv makes one, in a folder called.venv. source activates it, which switches this terminal over to it. Now python means the one inside that folder: which shows its path. pip installs openpyxl there, and the program saves the spreadsheet.

Ana's laptop
python3 -m venv .venv
Ana's laptop
source .venv/bin/activate
Ana's laptop
(.venv) ana@bakery:~/orders$ which python/home/ana/orders/.venv/bin/python
Ana's laptop
(.venv) ana@bakery:~/orders$ pip install openpyxlCollecting openpyxl
  Downloading openpyxl-3.1.5-py2.py3-none-any.whl.metadata (2.5 kB)
Collecting et-xmlfile (from openpyxl)
  Downloading et_xmlfile-2.0.0-py3-none-any.whl.metadata (2.7 kB)
Downloading openpyxl-3.1.5-py2.py3-none-any.whl (250 kB)
Downloading et_xmlfile-2.0.0-py3-none-any.whl (18 kB)
Installing collected packages: et-xmlfile, openpyxl
Successfully installed et-xmlfile-2.0.0 openpyxl-3.1.5
Ana's laptop
(.venv) ana@bakery:~/orders$ python3 totals.pySaved report.xlsx: 4 items

One more habit for this stage. pip freeze writes down every package in the environment, with its exact version, into a file called requirements.txt. Openpyxl, and the package it needed. On a new laptop, pip install -r requirements.txt reads that list and builds the same environment again.

Ana's laptop
(.venv) ana@bakery:~/orders$ pip freeze > requirements.txt
Ana's laptop
(.venv) ana@bakery:~/orders$ cat requirements.txtet_xmlfile==2.0.0
openpyxl==3.1.5

Stage 5: one project

Where do you go from here? Pick one direction, not all of them. Automation: scripts that do boring work, like Ana's report. The web: sites and APIs, ways for programs to talk, built with Django or FastAPI, two Python packages for the web. Data: pages and files, cleaned up into tables. Each has a full episode on this channel. The best first project is one you'd actually use.

Stage 5: pick one direction: cards of Automation, The web, Data, On a schedule, The best first project is one you'd actually use

What a project really teaches you is to read documentation. Ana didn't memorise openpyxl. Its own tutorial starts with the same import line she used, and the next example gives her the sheet to write into. Copy one small example, run it, then change it: that's how every package gets learned.

Stage 5: one project: browser screenshot of Ana's line 3

Ana's direction is automation. Here's what lands in the owner's spreadsheet: today's revenue per item. Croissants lead. And the cinnamon buns: three of them, at one ten each, shown as three thirty. Remember that number.

Ana's report: today's revenue per item: chart of Croissant: 39.6 $, Sourdough: 23.2 $, Rye loaf: 22.5 $, Cinnamon bun: 3.3 $, Cinnamon buns: 3 × 1.10, shown as 3.30

Stage 6: tests and Git

How do you know a program is still right next week? You write tests: small checks that run your code and compare the answer. Ana's says three cinnamon buns at one ten make three thirty. She installs pytest, which runs every test in files whose names start with test. Guess first: does it pass? It fails. Her program says three point three, and then fourteen more digits.

Ana's laptop
(.venv) ana@bakery:~/orders$ cat test_totals.pyfrom totals import revenue, totals

ORDERS = [{"item": "cinnamon bun", "qty": "3", "price": "1.10"}]


def test_totals():
    assert totals(ORDERS) == {"cinnamon bun": 3}


def test_revenue():
    assert revenue(ORDERS) == {"cinnamon bun": 3.30}
Ana's laptop
(.venv) ana@bakery:~/orders$ pip install pytestCollecting pytest
  Downloading pytest-9.2.0-py3-none-any.whl.metadata (7.9 kB)
Collecting iniconfig>=2 (from pytest)
  Downloading iniconfig-2.3.1-py3-none-any.whl.metadata (2.8 kB)
Collecting packaging>=24 (from pytest)
  Downloading packaging-26.3-py3-none-any.whl.metadata (3.5 kB)
Collecting pluggy<2,>=1.6 (from pytest)
  Downloading pluggy-1.7.0-py3-none-any.whl.metadata (5.0 kB)
Collecting pygments>=2.15 (from pytest)
  Downloading pygments-2.21.0-py3-none-any.whl.metadata (2.5 kB)
Downloading pytest-9.2.0-py3-none-any.whl (411 kB)
Downloading iniconfig-2.3.1-py3-none-any.whl (7.6 kB)
Downloading packaging-26.3-py3-none-any.whl (129 kB)
Downloading pluggy-1.7.0-py3-none-any.whl (27 kB)
Downloading pygments-2.21.0-py3-none-any.whl (1.3 MB)
Installing collected packages: pygments, pluggy, packaging, iniconfig, pytest
Successfully installed iniconfig-2.3.1 packaging-26.3 pluggy-1.7.0 pygments-2.21.0 pytest-9.2.0
Ana's laptop
(.venv) ana@bakery:~/orders$ pytest -q.F                                                               [100%]
=============================== FAILURES ===============================
_____________________________ test_revenue _____________________________

    def test_revenue():
>       assert revenue(ORDERS) == {"cinnamon bun": 3.30}
E       AssertionError: assert {'cinnamon bu...0000000000003} == {'cinnamon bun': 3.3}
E
E         Differing items:
E         {'cinnamon bun': 3.3000000000000003} != {'cinnamon bun': 3.3}
E         Use -v to get more diff

test_totals.py:11: AssertionError
======================= short test summary info ========================
FAILED test_totals.py::test_revenue - AssertionError: assert {'cinnam...
1 failed, 1 passed in 0.03s

This is the number we promised. Computers store most decimals in binary, slightly off, so three times one ten isn't exactly three thirty. The spreadsheet showed three point three, so nobody saw it. The test did. Ana adds round, to two places, to her code, as grep shows, and both tests pass.

Ana's laptop
(.venv) ana@bakery:~/orders$ python3Python 3.12.3 (main, Aug 31 2026, 10:18:26) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
Ana's laptop
>>> 3 * 1.103.3000000000000003
Ana's laptop
>>> round(3 * 1.10, 2)3.3
Ana's laptop
(.venv) ana@bakery:~/orders$ grep round totals.py        money[item] = round(money[item], 2)
Ana's laptop
(.venv) ana@bakery:~/orders$ pytest -q..                                                               [100%]
2 passed in 0.02s

The last habit is Git, a tool that tracks every version of a project. First, a file called gitignore tells it to leave the.venv folder out: it can be rebuilt any time. git init starts tracking, add with a dot picks every file, and each commit saves a snapshot, so she can always go back. Every command, and the lab files, are linked in the description.

Ana's laptop
(.venv) ana@bakery:~/orders$ echo .venv > .gitignore
Ana's laptop
(.venv) ana@bakery:~/orders$ git init -q
Ana's laptop
(.venv) ana@bakery:~/orders$ git add .
Ana's laptop
(.venv) ana@bakery:~/orders$ git commit -q -m "Order report with tests"
Ana's laptop
(.venv) ana@bakery:~/orders$ git log --onelinea45eef4 (HEAD -> main) Order report with tests

Can you change the order?

Can you change the order? A little. Git can start on your first day, and tests can come with your first project. But don't skip stage three: almost every question a beginner searches for is a traceback that hasn't been read from the bottom yet.

Recap

Six stages, each clearing a wall before you reach it.

If this gave your Python learning an order, subscribing brings each stage's next episode your way.

Every command here was typed into a real terminal, with Ubuntu's own Python. Thanks for watching Cloud Explainers.

Sources

Demos: a container on our own Mac standing in for a laptop at a bakery; the bakery is made up. openpyxl and pytest were downloaded from PyPI during the run. Watch the full video on YouTube.

More videos

Networking11 min

Every Networking Term a Beginner Needs in 12 Minutes

Docker6 min

How to Fix Docker 'Permission Denied' on docker.sock

Git7 min

How to Fix Git 'Updates Were Rejected' When You Push

Linux7 min

How to Fix nginx 502 Bad Gateway Errors on Linux

Linux8 min

How to Fix Linux apt 'Could Not Get Lock' Errors (Ubuntu)

Networking9 min

How Email Works on the Internet (SMTP) in 10 Minutes

Go (Golang)21 min

Golang REST API with Gin: Full Tutorial for Beginners

Networking10 min

How a WireGuard VPN Network Works

Linux9 min

Linux Makefiles Will Finally Make Sense After This

Auth (OAuth, JWT)9 min

SAML vs OAuth vs OpenID Connect: What's the Difference?

Python7 min

How to Fix Python cron Jobs That Never Run

Linux22 min

Linux Bash Scripting: Full Tutorial for Beginners

Networking10 min

How PXE Network Boot Works

Active Directory9 min

How Active Directory Works

Linux10 min

How to Fix a Linux systemd Service That Won't Start

Linux9 min

Linux Logs: syslog vs journald

Auth (OAuth, JWT)9 min

JWT vs Session Cookies: Which One Should You Use?

Docker10 min

Docker vs Docker Compose: Which One Should You Use?

Docker9 min

How to Fix Docker Compose Containers That Can't Talk to Each Other

Django (Python web framework)22 min

Django Full Tutorial for Beginners

Linux9 min

How Linux systemd Works

Networking8 min

How to Fix 'Windows Cannot Access' Network Share Errors (SMB)

APIs10 min

REST vs gRPC: Which One Should You Use?

Certificates & PKI (ADCS)9 min

How to Fix SSL Certificate Chain Errors (Missing Intermediate)

Python9 min

Every Python Concept for Beginners in 10 Minutes

Databases & SQL9 min

Every SQL Join Type in 10 Minutes

OpenRouter (one API for many AI models)9 min

Why OpenRouter Free Models Hit Their Limit So Fast

Gemini API & Google AI Studio9 min

Why Your Gemini API Free Tier Runs Out So Fast

OpenRouter (one API for many AI models)10 min

OpenRouter Will Finally Make Sense After This

Caching & Redis10 min

How Redis Caching Works

Gemini API & Google AI Studio10 min

Gemini API vs Vertex AI: Which One Should You Use?

Gemini API & Google AI Studio8 min

How to Fix a Leaked Gemini API Key

Go (Golang)9 min

Why Golang Uses Goroutines Instead of Threads

Gemini API & Google AI Studio9 min

How to Fix Gemini API Error 429: Resource Exhausted

Claude & the Claude Developer Platform10 min

Claude Token Pricing Will Finally Make Sense After This

Python11 min

Why Python Web Scraping Returns Nothing (and the Fix)

Python10 min

Python uv vs pip: Which One Should You Use?

Python22 min

Python Automation: Full Tutorial for Beginners

Auth (OAuth, JWT)9 min

How OAuth Login Works

Gemini API & Google AI Studio10 min

Google AI Studio Will Finally Make Sense After This

Docker10 min

What Happens When You Run a Docker Container

Claude & the Claude Developer Platform8 min

How to Fix Claude Code Billing Your API Key Instead of Your Plan

Auth (OAuth, JWT)9 min

Why Passkeys Can't Be Phished Like Passwords

PowerShell10 min

PowerShell vs Bash: Which One Should You Learn?

Linux10 min

The Linux Roadmap for Beginners

Django (Python web framework)9 min

Django vs FastAPI: Which One Should You Use?

Databases & SQL10 min

Why SQLite Is the Most Used Database in the World

Databases & SQL11 min

How a NoSQL Database Like MongoDB Works

Python11 min

How to Fix Python pip error: externally-managed-environment

Databases & SQL9 min

How to Fix MySQL Too Many Connections Database Errors

Go (Golang)9 min

Golang Concurrency Will Finally Make Sense After This

AWS10 min

How Amazon S3 Works

Kubernetes10 min

How Kubernetes Works

Git10 min

How Git Works

Docker10 min

How to Fix Docker Port Already Allocated Errors

PostgreSQL10 min

How PostgreSQL Works

Git8 min

How to Fix Git Merge Conflicts

Linux9 min

How to Fix No Space Left on Device in Linux

Networking10 min

How the Internet Works

Databases & SQL9 min

SQL vs NoSQL: When Each One Wins

Auth (OAuth, JWT)8 min

How to Fix JWT Token Expired Errors (401 Unauthorized)

Claude & the Claude Developer Platform9 min

Claude API vs Claude Pro: Why Your Subscription Doesn't Include It

AI agent skills9 min

Why AI Agent Skills Beat Giant Prompts

Linux10 min

Linux Cron Jobs Will Finally Make Sense After This

Cybersecurity10 min

Security Logs Will Finally Make Sense After This

Cybersecurity8 min

How to Fix a Security Leak: An API Key Pushed to GitHub

DNS9 min

DNS Will Finally Make Sense After This

Cybersecurity9 min

Never Ignore a Security Update

Auth (OAuth, JWT)9 min

Two-Factor Authentication Will Finally Make Sense

Networking8 min

Never Expose a Database to the Internet

Cybersecurity10 min

SSH Keys vs Passwords: Which Is Safer for Security?

Linux10 min

The 5 Levels of Linux Boot: From Power On to Login

Networking12 min

The 5 Levels of Network Firewalls

Cybersecurity10 min

Password Security: Why Hackers Crack Weak Passwords

Linux9 min

Why Your Linux Server Runs Out of Memory

Networking10 min

HTTP vs HTTPS: What Actually Changes on Your Network

Linux12 min

Every Linux File Permission You Need to Know

Linux10 min

Linux Processes vs Threads: When Each One Wins

Networking11 min

The OSI Model Will Finally Make Sense After This

Git11 min

The 7 Levels of Git

Networking10 min

Every Network Troubleshooting Command You Need

Cybersecurity10 min

Hashing vs Encryption in Cybersecurity: When Each One Wins

Networking11 min

Why the Internet Ran Out of IP Addresses

Linux11 min

Linux Package Managers: apt vs Snap vs Flatpak

Python9 min

Why Python Is Slow (and When It Doesn't Matter)

Networking11 min

TCP vs UDP: When Each One Wins

Linux8 min

Stop Running Linux as Root (Do This Instead)

Linux11 min

Linux Users and Groups Will Finally Make Sense

Cybersecurity9 min

Why Phishing Beats Good Security (and How to Spot It)

Docker12 min

The 7 Levels of Docker

AI agents & MCP12 min

AI Agents Will Finally Make Sense After This

Git8 min

Stop Using git push --force (Use This Instead)

AI agent skills10 min

Agent Skills vs MCP: When Each One Wins

Networking12 min

Networking Will Finally Make Sense After This

Linux12 min

Every Linux Command You Actually Need

Cybersecurity12 min

The 7 Levels of Cybersecurity

System design12 min

Every System Design Concept You Need, in One Diagram

PostgreSQL10 min

Why PostgreSQL Ignores Your Index