A Jupyter notebook lets you write Python in small cells and run them one at a time, with each result appearing right under its code instead of at the end of a whole file. That tight feedback loop is why data science happens in notebooks. This post covers cells and the kernel, installing JupyterLab, Google Colab, magics, inline plots, and the out-of-order trap that bites almost every beginner.
“A notebook is a lab bench, not a factory floor. You experiment here, then you move the working parts into a real program.”
Last Updated: July 2026 | Tested on: Python 3.14.6, JupyterLab 4.6 | Difficulty: Beginner | Reading Time: 16 minutes
Here is the everyday version. Think of a normal Python script as a recipe you hand to a cook all at once: they read the whole thing, make the dish, and only then tell you how it turned out. A notebook is more like cooking side by side with a friend, one step at a time. You chop the onions, taste, adjust, then move to the next step. If something is off, you fix that one step without starting the whole meal over. That is why data scientists live in notebooks: exploring data is all about taste-as-you-go.
The piece that makes this work, and the piece beginners never see, is the kernel. When you open a notebook, a real Python process starts in the background and stays running. Every time you run a cell, that cell’s code is sent to the kernel, the kernel runs it, remembers every variable you created, and sends the result back to be shown under the cell. The notebook you see in the browser is just a friendly front end. The kernel is where your program actually lives. Keep that split in your head and everything else in this post clicks.
Read the diagram top to bottom. Cells on top send their code to one long-lived kernel when you press Shift+Enter. The kernel keeps a namespace of live variables and an execution counter, the little In[1], In[2] numbers that count the order you ran cells, not the order they sit on the page. Output comes back under the cell. And when things get confusing, Restart Kernel wipes the namespace clean and sets the counter back to In[1]. That restart button is the single most useful thing to know in this whole post.
Table of Contents
What a Notebook Actually Is: Cells and the Kernel
A notebook file has the extension .ipynb, and despite the fancy display it is just a JSON text file listing your cells and their saved outputs. There are two cell types you use constantly: code cells that run Python, and Markdown cells that hold formatted notes, headings, and explanations. You run a code cell with Shift+Enter, and the result shows up immediately below it. The last expression in a cell is auto-displayed, so you rarely need print() just to peek at a value.
The mental model that saves you: the kernel is one running Python session shared by every cell. A variable you create in the top cell is still there fifty cells later, because it is all one process. This is a gift when you are exploring, and a trap when you lose track of what you ran. We will trigger that trap on purpose in a minute so you never fall for it by accident.
Installing Jupyter in a Virtual Environment
The clean way to install Jupyter is inside a virtual environment, so the notebook tooling lives in the same box as the libraries your project uses. Create a venv, activate it, then install JupyterLab, which is the modern browser interface for notebooks.
📄 Terminal: install JupyterLab inside a venv
# 1. Make a project folder and a virtual environment python -m venv .venv # 2. Activate it (Linux/macOS) source .venv/bin/activate # Windows PowerShell: .venv\Scripts\Activate.ps1 # 3. Install the notebook interface plus a couple of data libraries pip install jupyterlab numpy matplotlib # 4. Confirm what you got jupyter --version # 5. Launch it (opens in your browser at http://localhost:8888) jupyter lab
▶ Output of jupyter –version
Selected Jupyter core packages... IPython : 9.15.0 ipykernel : 7.3.0 jupyter_client : 8.9.1 jupyter_core : 5.9.1 jupyter_server : 2.20.0 jupyterlab : 4.6.1 nbclient : 0.11.0 nbconvert : 7.17.1 nbformat : 5.10.4
What happened here: one pip install jupyterlab pulled in a small stack of packages, and jupyter --version lists them. The two that matter most are ipykernel, which is the Python kernel your cells run against, and jupyterlab, the interface. Running jupyter lab then starts a tiny local web server and opens the notebook interface in your browser. Nothing is uploaded anywhere; the server is running on your own machine at localhost.
If you would rather not install anything at all, skip ahead to the Colab section, which gives you the same notebook in a browser tab with zero setup. Jupyter is the tool we use here, but Google Colab, Kaggle Notebooks, and the notebook editor built into VS Code all run the exact same .ipynb files, so at the time of writing you can move between them freely.
The Out-of-Order Trap Every Beginner Hits
Because the kernel remembers everything, the number in In[n] is the order you ran cells, not the order they appear on screen. Run a cell twice and its code executes twice against the same variables. Say a learner named Aditi is counting apples in a basket. She writes one cell that sets the basket to 100, and a second cell that removes 30. Then, out of habit, she keeps hitting Shift+Enter on that second cell. Here is exactly what the kernel does, printed with the real execution counter.
📄 What the kernel runs when you re-run the same cell
# Cell A (you run this once)
apples = 100
# Cell B (you run this... several times, without re-running Cell A)
apples = apples - 30
print('basket:', apples)
▶ Output (watch the In[n] counter and the value)
In [1]: apples = 100
In [2]: apples = apples - 30
-> basket: 70
In [3]: apples = apples - 30
-> basket: 40
In [4]: apples = apples - 30
-> basket: 10
In [5]: print('final apples:', apples)
-> final apples: 10
What happened here: Cell A ran once as In[1]. Then Cell B ran three separate times as In[2], In[3], and In[4], and each run subtracted another 30 from whatever apples already was: 70, then 40, then 10. The code on the page still reads “subtract 30 once,” but the kernel has run it three times, so the live value is 10, not 70. This is the out-of-order trap. Your screen shows one thing, the kernel’s memory holds another.
The fix is a habit: when a notebook starts feeling haunted, click Kernel → Restart Kernel and Run All Cells. That throws away the hidden state and runs every cell once, top to bottom, so what you see matches what actually happened. A notebook that cannot survive a clean top-to-bottom run is a notebook with a bug.
Markdown, Magics, and Inline Plots
Three notebook features do most of the heavy lifting. Markdown cells let you write headings, bullet points, and explanations between your code, so a notebook reads like a lab report rather than a wall of code. Magics are special commands starting with % or %% that the kernel understands; they are notebook-only shortcuts, not Python. And inline plots mean a chart appears right under the cell that drew it. Here are the two magics you will reach for most: %timeit to measure one line, and %%time to measure a whole cell.
📄 Notebook cells: two timing magics
# One line magic: runs the statement many times and reports the average
%timeit sum(range(10_000))
# One cell magic: times everything in this cell once
%%time
total = 0
for i in range(2_000_000):
total += i
print('total:', total)
▶ Output
139 μs ± 3.61 μs per loop (mean ± std. dev. of 7 runs, 10,000 loops each) total: 1999999000000 CPU times: total: 219 ms Wall time: 217 ms
What happened here: %timeit is smart. It ran sum(range(10_000)) thousands of times, threw out the noise, and reported a stable average of about 139 microseconds per loop with a small spread. That is far more trustworthy than timing one run with a stopwatch. %%time is the whole-cell version: it ran the two-million-step loop once and reported roughly 217 milliseconds of wall time, the real clock time you actually waited. Reach for %timeit when you want to compare two small approaches fairly, and %%time when you just want to know how long a chunk took.
Plotting is the other everyday win. Draw a chart with Matplotlib and it renders inline, right inside the notebook, no separate window.
📄 Notebook cell: an inline chart
import matplotlib.pyplot as plt
sales = [12, 19, 9, 22, 15] # tomato punnets sold, Mon to Fri
plt.plot(sales, marker='o')
plt.title('Tomato punnets sold')
plt.show()
▶ Output (rendered under the cell)
<Figure size 640x480 with 1 Axes> # a line chart with five points appears inline, saved inside the .ipynb as a PNG
What happened here: the chart drew directly under the cell, and here is the part people miss: the image is saved into the .ipynb file as an embedded PNG. When you executed this notebook, the output really did contain an image/png payload alongside the text. That is why a notebook shared on GitHub still shows its charts even before anyone runs it. A quick keyboard note while we are here: press Esc then A to add a cell above, Esc then B to add below, Esc then M to turn a cell into Markdown, and Esc then D D (D twice) to delete a cell. Those five shortcuts cover most of what you do all day.
Notebook or Script? When to Use Each
A Jupyter notebook and a plain .py script are not rivals; they are different tools for different moments. A notebook wins when you are exploring: poking at data, trying a chart, testing an idea one cell at a time. A script wins when you are shipping: a program that other code imports, runs on a schedule, or deploys to a server. The honest rule is that exploration lives in a notebook and the final product moves into scripts. This is exactly why the capstone projects later in this series ship as Git repositories full of .py files with tests, not as a single notebook.
When you do need to convert, two tools handle it. jupyter nbconvert exports a notebook to a script, and jupytext can keep a notebook and a plain-text .py version paired so the script is easy to read in Git diffs. Both are real commands you can run right now.
📄 Terminal: convert a notebook to a script
# Straight export to a runnable .py file jupyter nbconvert --to script demo.ipynb # Or pair it with a clean, diff-friendly percent-format script jupytext --to py:percent demo.ipynb -o demo_paired.py
▶ Output: demo_paired.py
# %% [markdown]
# # Fruit Sales Explorer
# A quick look at this week's stall numbers.
# %%
import numpy as np
prices = np.array([12, 18, 9, 22, 15])
print('mean price:', prices.mean())
# %%
# %timeit sum(range(10_000))
What happened here: jupytext turned each cell into a block marked with # %%, and Markdown cells became # %% [markdown] comments. Notice the magic line came out as # %timeit ..., commented out, because magics are notebook-only and would not run as plain Python. This percent format is lovely in Git: it is a normal text file, so code review and diffs work properly, yet many editors can still open it as a notebook. It is the bridge between the explore-freely world and the ship-carefully world.
Google Colab: Zero Install, Sessions, and the GPU Toggle
Google Colab is a free, hosted notebook that runs in your browser with nothing to install. You open colab.research.google.com, sign in with a Google account, and you are typing Python into cells within seconds. Under the hood it is the same Jupyter notebook idea, the same kernel, the same .ipynb file. The only real differences are where it runs and how you save.
A few Colab specifics worth knowing up front. Your notebook runs on a temporary virtual machine that Google lends you, and that machine is not permanent: sessions time out after a stretch of inactivity or after a maximum runtime, and when they do, any files you downloaded onto the machine disappear. Your notebook itself is safe because it saves to Google Drive, but re-downloaded data and installed packages are gone and must be set up again next session. To install a package inside a Colab cell, you run !pip install pandas with a leading exclamation mark, which runs a shell command instead of Python.
📄 A typical first Colab cell
# The leading ! runs a shell command, not Python
!pip install --quiet pandas
# Save your work: File -> Save a copy in GitHub, or it autosaves to Drive
import sys
print('running on:', sys.version.split()[0])
What to remember: Colab hands you a free Graphics Processing Unit (GPU) too, which you turn on under Runtime → Change runtime type → GPU. You will not need it for a while; the classic machine learning and pandas work early in this series runs fine on the plain Central Processing Unit (CPU) runtime. When the deep learning chapters arrive, that toggle becomes the difference between waiting minutes and waiting hours, and there is a full walkthrough of it later in the series.
For now, just know it exists. Colab is the primary free host at the time of writing, with Kaggle Notebooks offering a similar free GPU on a weekly quota, so you always have a backup if one is busy.
Common Mistakes
❌ Mistake 1: Trusting a notebook you never ran top to bottom
# You edited Cell 1 to load a new file, but only re-ran Cell 5. # The kernel still holds the OLD data. Your "results" are a lie. # FIX: Kernel -> Restart Kernel and Run All Cells before you trust anything.
❌ Mistake 2: Deleting a cell and assuming its variables are gone
# You delete the cell that defined secret = "abc123" # But the kernel still remembers secret until you restart. print(secret) # still works, still "abc123" -> confusing later
Why this matters: both mistakes come from the same root cause, the kernel’s hidden memory. Deleting a cell removes the code from the page but not the variable from the running process. Editing a cell without re-running it means the page and the kernel disagree. The one reflex that prevents both is restarting and running all cells before you believe a result or share a notebook. If it does not survive a clean run, it is not done.
Best Practices
- Restart and run all before trusting output. Make it a reflex, especially before sharing or committing a notebook.
- Install Jupyter inside a venv, so the notebook and your project libraries share one clean box. Colab handles this for you.
- Keep cells small. One idea per cell makes re-running and debugging painless.
- Explore in notebooks, ship in scripts. Move working code into
.pyfiles with tests once it settles. - Use Markdown cells generously. A notebook that explains itself is worth ten that just compute.
Wrapping Up
You now understand the thing that confuses every notebook beginner: a Jupyter notebook is a friendly front end over a long-lived kernel that remembers all your variables, and the In[n] numbers count run order, not page order. You can install it in a venv or open Colab with zero setup, you know the two timing magics and inline plotting, you can convert notebooks to scripts with nbconvert or jupytext, and you know the one habit, restart and run all, that keeps your results honest. Next up we put this to work on real, messy data in the pandas introduction. And if you want to jump to any other topic, browse the full Python + AI/ML tutorial series home.
Frequently Asked Questions
What is a Jupyter notebook used for?
A Jupyter notebook is used for interactive coding, where you run Python in small cells and see each result immediately. It is the standard tool for data science, machine learning experiments, and teaching, because it mixes runnable code, charts, and written notes in one document.
What is the kernel in a Jupyter notebook?
The kernel is the live Python process that runs your cells and remembers every variable you create. The notebook you see in the browser is just the front end; the kernel is where the code actually executes. Restarting the kernel wipes all remembered variables and resets the execution counter.
Do I need to install Jupyter to use notebooks?
No. You can install JupyterLab locally with pip install jupyterlab, or use a hosted notebook like Google Colab or Kaggle Notebooks that runs in your browser with nothing to install. All of them open the same .ipynb files.
Why does my Jupyter cell show the wrong value?
Almost always because cells were run out of order or a cell was re-run, so the kernel’s remembered variables no longer match the code on the page. Fix it with Kernel then Restart Kernel and Run All Cells, which runs everything once from top to bottom.
What is the difference between a notebook and a Python script?
A notebook runs code cell by cell with saved outputs and is ideal for exploring. A .py script is a plain program meant to be imported, deployed, or scheduled. Explore in notebooks, then move settled code into scripts with tests, which is why capstone projects ship as repos, not notebooks.
Interview Questions on Jupyter Notebooks
These come from real screens and onsites. Practice answering before you read each answer.
Q: A colleague sends you a notebook and says “the results are wrong on my machine.” What is the first thing you check?
Whether the notebook runs cleanly top to bottom. Do Kernel then Restart Kernel and Run All Cells. Most “wrong on my machine” notebook bugs are hidden-state bugs: cells were run out of order, or a cell was edited but not re-run, so the saved outputs reflect a kernel state that no longer matches the code. A clean restart-and-run-all either reproduces the correct result or surfaces the real error. If it only works when run in a weird order, the notebook itself is broken and needs fixing, not the machine.
Q: What does the number in In[7] next to a cell actually mean?
It is the execution count: this cell was the seventh cell executed in the current kernel session, regardless of where it sits on the page. It is not a line number and not a position. A cell higher on the page can show a larger number than one below it if you ran it later. When you see the counts out of sequence, that is your signal the notebook was run out of order and might be holding stale state. Restarting the kernel resets the counter to one.
Q: When would you choose a plain .py script over a notebook?
Anytime the code needs to be imported, tested, scheduled, or deployed. Notebooks are excellent for exploration and communication, but they carry hidden state, are awkward to diff in Git, and do not import cleanly as modules. Production code, libraries, and anything covered by automated tests belong in scripts. A common workflow is to prototype in a notebook, then move the settled logic into .py files, using nbconvert or jupytext to help with the move.
Q: What is the difference between %timeit and %%time?
Both are notebook magics for measuring speed, but they differ in scope and rigor. %timeit is a line magic that runs a single statement many times, discards outliers, and reports a stable average with a spread, which makes it the right tool for comparing two small approaches fairly. %%time is a cell magic that runs the entire cell once and reports the wall-clock and CPU time for that single run. Use %timeit for careful micro-benchmarks and %%time for a quick “how long did this whole block take” check.
Q: In Google Colab, why do your installed packages and downloaded files disappear between sessions?
Because Colab runs your notebook on a temporary virtual machine that Google reclaims after inactivity or a maximum runtime. The notebook file itself is saved to Google Drive or GitHub, so your code survives, but the machine’s local filesystem, including anything installed with !pip install or downloaded during the session, is wiped when the session ends. The practical fix is to keep setup steps at the top of the notebook so a fresh session rebuilds itself, and to save important data to Drive rather than the temporary disk.
Go deeper: the official Python documentation covers every edge case of this topic.
Related Posts
Previous: Python Interview Questions: The 40 That Actually Get Asked
Next: Python: Math for Data Science, The Only Math You Need
Series Home: Python + AI/ML Tutorial Series

No comment