Python, de la zero Lecția 47 / 60

Jupyter, notebook-uri si momentul in care le parasesti

De ce notebook-urile sunt seducatoare, unde stralucesc si momentul in care ar trebui sa te opresti si sa scrii un script real.

Un notebook este un lucru profund seducător. Scrii o linie, apeși Shift+Enter, vezi rezultatul. Mai scrii una, mai vezi unul. O oră mai târziu ai o analiză funcțională fără să te fi gândit vreodată la structura fișierelor, la granițele funcțiilor sau la importuri. Notebook-urile sunt cel mai aproape de fluența unui spreadsheet la care ajunge Python.

Sunt și cel mai aproape de a fi un pistol îndreptat spre piciorul tău, fiindcă aceeași proprietate care le face seducătoare (kernelul care păstrează starea între celule) este proprietatea care îți putrezește în liniște munca.

Lecția asta este despre ambele fețe. Unde își câștigă notebook-urile reputația, unde eșuează și momentul în care portezi codul într-un fișier .py.

Ce este de fapt un notebook

Un notebook Jupyter este un fișier JSON cu extensia .ipynb. Conține o listă ordonată de celule, fiecare fiind cod sau markdown. Când execuți o celulă de cod, textul este trimis unui kernel (un proces Python care rulează în fundal) care îl evaluează și returnează rezultatul. Rezultatul, inclusiv stdout, stderr, valoarea de retur și orice reprezentare bogată (grafice, tabele, HTML), este stocat înapoi în JSON.

Trei lucruri rezultă din designul ăsta:

  1. Kernelul are stare. O variabilă definită în celula 3 e vizibilă în celula 7.
  2. Poți executa celulele în orice ordine. Celula 7 poate rula înainte de celula 3.
  3. Output-ul salvat în fișier este output-ul de la ultima rulare a celulei, nu neapărat output-ul rulării întregului notebook de sus în jos.

Recitește astea trei. Împreună explică aproape orice poveste de groază cu notebook-uri pe care ai auzit-o vreodată.

Unde strălucesc notebook-urile

Explorare: ai un CSV, vrei să știi ce e în el. Îl încarci, .head(), .describe(), plotezi o histogramă, faci slicing pe o coloană. Bucla de feedback editare-rulare-vizualizare este de neîntrecut.

import pandas as pd

df = pd.read_csv("sales.csv")
df.head()

Predare: un notebook cu text, cod și output intercalat este un artefact complet. Cititorul vede ce ai scris și ce a ieșit.

Analiză pe care o vei preda unui stakeholder: render în HTML sau PDF, trimiți link-ul, gata. Destinatarul vede cod, rezultate și proză într-un singur document.

Prototipare ML: bucle de antrenament pe care vrei să le inspectezi în timpul rulării, grafice intermediare, hiperparametri pe care îi reglezi manual. Faptul că kernelul rămâne viu între celule înseamnă că nu reîncarci modelul de fiecare dată când schimbi un learning rate.

Unde eșuează notebook-urile

Cod de producție: notebook-urile sunt aproape imposibil de programat, monitorizat sau testat. Poți rula un notebook din cron cu papermill, dar în clipa în care o faci ai învelit o unealtă interactivă cu stare într-un harness de script, și acum ai două probleme.

Controlul versiunilor: formatul JSON include output-uri de celule, contoare de execuție și metadate. Două persoane care rulează același notebook produc fișiere diferite chiar dacă codul e identic. Diff-urile Git sunt ilizibile. Există unelte care să elimine output-urile (nbstripout), dar trebuie să ții minte să le instalezi în fiecare clonă.

Biblioteci reutilizabile: o funcție definită în celula 4 nu e importabilă din alt notebook fără copy-paste. Poți scrie %run other.ipynb ca să execuți alt notebook în kernelul curent, dar acum graful tău de dependențe este invizibil.

Stare ascunsă: ucigașul. Definești df în celula 1, îl modifici în celula 3, apoi te întorci la celula 2 și o rulezi din nou. Variabila are încă modificările din celula 3. Șase luni mai târziu cineva rulează notebook-ul de sus în jos și obține numere diferite. Crede că datele s-au schimbat. Datele nu s-au schimbat. S-a schimbat ordinea celulelor.

Soluția pentru starea ascunsă e simplă în principiu: restart and run all înainte să ai încredere în rezultat, înainte să faci commit, înainte să distribui. Fă-o reflex.

Cele două IDE-uri în 2026

Jupyter Lab este mediul original bazat pe web. Pornești un server (jupyter lab), deschizi un browser, primești un arbore de fișiere în stânga și taburi de notebook-uri în centru. Este încă cea mai curată experiență pentru muncă pură de notebook, în special cu extensii precum jupyterlab-git și inspectorul de variabile.

Notebook-uri VS Code (de asemenea Cursor, Zed și alte fork-uri VS Code) au deschis ușa pentru o generație de dezvoltatori care deja trăiau în editor. Deschizi direct un .ipynb, editorul îl randează inline și primești experiența completă a IDE-ului: type checking, refactoring, terminal integrat, controlul versiunilor. În 2026 asta este alegerea dominantă pentru majoritatea dezvoltatorilor cu care lucrez, iar motivul e simplu: ești la un scurtcircuit de tastatură distanță de fișierele tale .py în aceeași fereastră.

Alege oricare îți place. Ambele sunt bune. Formatul fișierului notebook este identic, deci poți comuta între ele.

Igiena kernelului

Numește kernelul după mediul virtual al proiectului. Dintr-un venv activat al unui proiect:

pip install ipykernel
python -m ipykernel install --user --name myproject --display-name "Python (myproject)"

Acum Python (myproject) apare în selectorul de kernel. Trei motive pentru care contează:

  1. Nu vei importa accidental pachete din mediul greșit.
  2. Notebook-ul înregistrează ce kernel așteaptă, deci colaboratorii văd numele corect.
  3. Poți avea un kernel per proiect fără să poluezi Python-ul global.

Când ceva nu mai merge și nu îți dai seama de ce, restartează kernelul. Jumătate din toate bug-urile „ciudate” de notebook sunt obiecte vechi în memorie.

Tiparul percent-cell

Cel mai bine păstrat secret al tooling-ului Python modern: un fișier .py obișnuit cu marcatori # %% se comportă ca un notebook în VS Code, Cursor, PyCharm și Spyder.

# %% Imports
import pandas as pd
import numpy as np

# %% Load data
df = pd.read_csv("sales.csv")
df.head()

# %% Quick stats
df.describe()

# %% Plot
df["revenue"].hist(bins=50)

Deschide ăsta în VS Code, dă click pe „Run Cell” deasupra oricărui bloc # %% și obții o fereastră interactivă cu output, exact ca un notebook. Fișierul în sine este Python pur. Diff-urile funcționează. Importurile funcționează. Îl poți rula ca script cu python file.py. Poți scrie teste pentru funcțiile din el.

La asta apelez implicit acum. Scriu fișiere .py cu marcatori de celulă, pic în fereastra interactivă când vreau să explorez, iar fișierul rămâne suficient de curat cât să facă commit. Apelez la .ipynb doar când am nevoie de output randat pentru altcineva care să citească.

Unelte care merită știute

nbdev tratează notebook-urile ca sursă de adevăr pentru o bibliotecă. Scrii implementarea, documentația și testele în același .ipynb, iar nbdev generează un pachet .py și documentație HTML. Unele echipe jură pe el. L-am văzut funcționând și l-am văzut devenind o mlaștină nementenabilă. Răspunsul cinstit: dacă ești deja o persoană notebook-first, încearcă-l; dacă nu, nu.

papermill parametrizează rulările de notebook:

papermill analysis.ipynb output.ipynb -p year 2025 -p region "EU"

Injectează parametri într-o celulă etichetată și rulează tot notebook-ul fără interfață. Util pentru rapoarte în lot.

jupyter nbconvert transformă un notebook în HTML, PDF sau script:

jupyter nbconvert --to html report.ipynb
jupyter nbconvert --to pdf report.ipynb
jupyter nbconvert --to script report.ipynb  # îți dă un .py

Exportul HTML este modul corect de a partaja rezultate cu un stakeholder netehnic.

Când părăsești notebook-ul

Țin un mic arbore de decizie mental. Când oricare dintre acestea e adevărat, notebook-ul trebuie să devină un fișier .py sau un modul propriu-zis:

  • Mă trezesc rulând același notebook pe inputuri diferite mai mult de două ori.
  • Un coleg trebuie să-l ruleze și să obțină același răspuns în mod fiabil.
  • Logica este copiată în alt notebook.
  • E suficient de lent încât vreau să-l programez.
  • Conține o funcție pe care vreau s-o folosesc în altă parte.

Calea de migrare e de obicei: factorizezi logica grea într-un modul .py, apoi păstrezi un notebook subțire care îl importă și randează output-ul.

# analysis.py
def load_clean(path: str) -> pd.DataFrame:
    df = pd.read_csv(path)
    df = df.dropna(subset=["revenue"])
    return df

def summarize(df: pd.DataFrame) -> pd.DataFrame:
    return df.groupby("region")["revenue"].agg(["mean", "median", "count"])
# report.ipynb (sau report.py cu marcatori # %%)
from analysis import load_clean, summarize

df = load_clean("sales.csv")
summarize(df)

Notebook-ul devine un strat de prezentare. Logica trăiește în cod pe care îl poți testa.

Magics care merită știute

Comenzile magice ale Jupyter sunt comenzi scurtătură prefixate cu % (linie) sau %% (celulă). Câteva merită păstrate în memoria musculară:

  • %timeit expr și %%timeit măsoară timpul de execuție cu warmup și repetare corecte. Mult mai bun decât să-ți rostogolești propriile apeluri time.time().
  • %load_ext autoreload urmat de %autoreload 2 înseamnă că modulele tale .py importate sunt re-importate automat când le editezi. E cea mai mare singură setare de calitate a vieții pentru fluxul percent-cell.
  • %env VAR=value setează variabilele de mediu pentru kernel.
  • %%writefile name.py scrie conținutul unei celule într-un fișier, util când ai prototipat o funcție într-un notebook și vrei s-o extragi.
  • %debug te aruncă într-un debugger post-mortem după o excepție. Combinat cu breakpoint() din lecția anterioară, ai o poveste de debugging completă fără să părăsești kernelul.

Nu trebuie să le memorezi pe celelalte. %lsmagic listează tot ce e disponibil.

Distribuire și reproductibilitate

Cea mai mare slăbiciune a unui notebook ca livrabil este că destinatarul are nevoie de același mediu ca să-l ruleze din nou. Trei tipare ajută:

  1. Include un requirements.txt sau pyproject.toml lângă notebook. Specifică explicit versiunea Python.
  2. Fixează versiunile bibliotecilor care au condus analiza, în special pandas și NumPy, fiindcă API-urile lor evoluează.
  3. Randează în HTML pentru consumatorii doar-citire. Nu trebuie să ruleze nimic; trebuie doar să citească.

Pentru colaboratorii care vor re-rula, uv (pe care l-am acoperit mai devreme în curs) face setup-ul rapid trivial. Un README care zice „rulează uv sync && jupyter lab” e suficient.

Verdictul cinstit

Notebook-urile sunt o unealtă strălucită pentru prima jumătate a oricărui proiect și unealta greșită pentru a doua jumătate. Prima jumătate e „ce e în datele astea, care e forma, care e povestea”. A doua jumătate e „fă să ruleze fiabil pentru totdeauna”. Schimbi uneltele când schimbi fazele.

Dacă scrii notebook-uri de ani de zile și opui rezistență fișierelor .py, încearcă tiparul percent-cell pentru o săptămână. Vei obține feedback-ul interactiv pe care îl iubești și diff-urile de care viitorul tău eu are nevoie. Lecția următoare pune toate astea (pandas, NumPy, SciPy și fluxul pe care tocmai l-am schițat) într-un singur proiect end-to-end.

Caută