Introduction to Jupyter Notebook¶
A Jupyter notebook combines executable code, formatted explanations, equations, figures, and results in one document. This makes notebooks useful for exploring a problem and documenting a scientific calculation.
A notebook has two distinct parts:
- the
.ipynbfile, which stores cells, metadata, and optionally their saved outputs; - a kernel, which is a running Python process that executes the code.
The notebook document can remain open after its kernel has stopped, and closing a browser tab does not necessarily stop the kernel.
Code cells and Markdown cells¶
The two cell types used most often are:
- Code cells, which are sent to the kernel for execution;
- Markdown cells, which contain formatted text, links, equations, and images.
This is a Markdown cell. Markdown supports:
- italic and bold text;
- numbered and bulleted lists;
- inline code such as
np.linspace; - links;
- inline mathematics, such as $E=mc^2$;
- displayed mathematics:
\begin{equation} \int_0^\infty e^{-x}\,dx=1. \end{equation}
An image copied from Preview can be pasted directly into a Markdown cell. Jupyter stores it as an attachment inside the notebook.
Command mode and edit mode¶
A selected cell can be in one of two modes:
- Edit mode: type inside the cell. Press
Enteror double-click a cell to enter edit mode. - Command mode: operate on the cell as a whole. Press
Escto enter command mode.
Common execution shortcuts are:
Shift-Enter: run the cell and select the next cell;Ctrl-Enter: run the cell and remain on it;Alt-Enter: run the cell and insert a new cell below.
Common command-mode shortcuts are:
a/b: insert a cell above / below;m/y: change the cell to Markdown / code;c,x,v: copy, cut, and paste cells;dd: delete a cell;z: undo deletion of a cell.
Shortcuts can differ slightly between Jupyter Notebook and JupyterLab. Use the Command Palette or the keyboard-shortcuts menu to see the authoritative list for the interface you are running.
Executing Python¶
Only the final unassigned expression in a code cell is displayed automatically. Use print or display when you want to show several results.
import numpy as np
print("cos(pi) =", np.cos(np.pi))
print("2 pi =", 2*np.pi)
np.zeros(5) # The final expression is displayed automatically.
cos(pi) = -1.0 2 pi = 6.283185307179586
array([0., 0., 0., 0., 0.])
A semicolon suppresses the automatic display of the final expression:
np.ones(5);
The kernel is stateful¶
Variables, imports, and function definitions remain in the kernel until they are deleted or the kernel is restarted. Execution counts such as In [4] record the order in which cells were run—not their position in the notebook.
radius = 3.0
area = np.pi*radius**2
area
28.274333882308138
print(area)
del area
print(area)
28.274333882308138
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[9], line 3 1 print(area) 2 del area ----> 3 print(area) NameError: name 'area' is not defined
# After the first execution, remove the next line and run this cell repeatedly.
counter = 0
counter += 1
counter
1
After the preceding cell has been executed once, try deleting the line counter = 0 and running it repeatedly. This demonstrates why running cells out of order can make a notebook difficult to reproduce.
A reliable notebook should run correctly from top to bottom in a fresh kernel. Before sharing important work, use Restart Kernel and Run All Cells. If that fails, the notebook was relying on hidden state.
Completion, documentation, and inspection¶
Jupyter provides several ways to discover an unfamiliar library:
- type part of a name and press
Tabfor completion; - place the cursor inside a function call and press
Shift-Tabto inspect its signature; - append
?to a name for documentation; - append
??to request additional details and source code when available; - use Python's
help(...)anddir(...)functions.
Focused inspection is generally more useful than printing every name in a large module.
from scipy import integrate
integrate.quad?
Signature: integrate.quad( func, a, b, args=(), full_output=0, epsabs=1.49e-08, epsrel=1.49e-08, limit=50, points=None, weight=None, wvar=None, wopts=None, maxp1=50, limlst=50, complex_func=False, ) Docstring: Compute a definite integral. Integrate func from `a` to `b` (possibly infinite interval) using a technique from the Fortran library QUADPACK. Parameters ---------- func : {function, scipy.LowLevelCallable} A Python function or method to integrate. If `func` takes many arguments, it is integrated along the axis corresponding to the first argument. If the user desires improved integration performance, then `f` may be a `scipy.LowLevelCallable` with one of the signatures:: double func(double x) double func(double x, void *user_data) double func(int n, double *xx) double func(int n, double *xx, void *user_data) The ``user_data`` is the data contained in the `scipy.LowLevelCallable`. In the call forms with ``xx``, ``n`` is the length of the ``xx`` array which contains ``xx[0] == x`` and the rest of the items are numbers contained in the ``args`` argument of quad. In addition, certain ctypes call signatures are supported for backward compatibility, but those should not be used in new code. a : float Lower limit of integration (use -numpy.inf for -infinity). b : float Upper limit of integration (use numpy.inf for +infinity). args : tuple, optional Extra arguments to pass to `func`. full_output : int, optional Non-zero to return a dictionary of integration information. If non-zero, warning messages are also suppressed and the message is appended to the output tuple. complex_func : bool, optional Indicate if the function's (`func`) return type is real (``complex_func=False``: default) or complex (``complex_func=True``). In both cases, the function's argument is real. If full_output is also non-zero, the `infodict`, `message`, and `explain` for the real and complex components are returned in a dictionary with keys "real output" and "imag output". Returns ------- y : float The integral of func from `a` to `b`. abserr : float An estimate of the absolute error in the result. infodict : dict A dictionary containing additional information. message A convergence message. explain Appended only with 'cos' or 'sin' weighting and infinite integration limits, it contains an explanation of the codes in infodict['ierlst'] Other Parameters ---------------- epsabs : float or int, optional Absolute error tolerance. Default is 1.49e-8. `quad` tries to obtain an accuracy of ``abs(i-result) <= max(epsabs, epsrel*abs(i))`` where ``i`` = integral of `func` from `a` to `b`, and ``result`` is the numerical approximation. See `epsrel` below. epsrel : float or int, optional Relative error tolerance. Default is 1.49e-8. If ``epsabs <= 0``, `epsrel` must be greater than both 5e-29 and ``50 * (machine epsilon)``. See `epsabs` above. limit : float or int, optional An upper bound on the number of subintervals used in the adaptive algorithm. points : (sequence of floats,ints), optional A sequence of break points in the bounded integration interval where local difficulties of the integrand may occur (e.g., singularities, discontinuities). The sequence does not have to be sorted. Note that this option cannot be used in conjunction with ``weight``. weight : float or int, optional String indicating weighting function. Full explanation for this and the remaining arguments can be found below. wvar : optional Variables for use with weighting functions. wopts : optional Optional input for reusing Chebyshev moments. maxp1 : float or int, optional An upper bound on the number of Chebyshev moments. limlst : int, optional Upper bound on the number of cycles (>=3) for use with a sinusoidal weighting and an infinite end-point. See Also -------- :func:`dblquad` double integral :func:`tplquad` triple integral :func:`nquad` n-dimensional integrals (uses `quad` recursively) :func:`fixed_quad` fixed-order Gaussian quadrature :func:`simpson` integrator for sampled data :func:`romb` integrator for sampled data :func:`scipy.special` for coefficients and roots of orthogonal polynomials Notes ----- For valid results, the integral must converge; behavior for divergent integrals is not guaranteed. **Extra information for quad() inputs and outputs** If full_output is non-zero, then the third output argument (infodict) is a dictionary with entries as tabulated below. For infinite limits, the range is transformed to (0,1) and the optional outputs are given with respect to this transformed range. Let M be the input argument limit and let K be infodict['last']. The entries are: 'neval' The number of function evaluations. 'last' The number, K, of subintervals produced in the subdivision process. 'alist' A rank-1 array of length M, the first K elements of which are the left end points of the subintervals in the partition of the integration range. 'blist' A rank-1 array of length M, the first K elements of which are the right end points of the subintervals. 'rlist' A rank-1 array of length M, the first K elements of which are the integral approximations on the subintervals. 'elist' A rank-1 array of length M, the first K elements of which are the moduli of the absolute error estimates on the subintervals. 'iord' A rank-1 integer array of length M, the first L elements of which are pointers to the error estimates over the subintervals with ``L=K`` if ``K<=M/2+2`` or ``L=M+1-K`` otherwise. Let I be the sequence ``infodict['iord']`` and let E be the sequence ``infodict['elist']``. Then ``E[I[1]], ..., E[I[L]]`` forms a decreasing sequence. If the input argument points is provided (i.e., it is not None), the following additional outputs are placed in the output dictionary. Assume the points sequence is of length P. 'pts' A rank-1 array of length P+2 containing the integration limits and the break points of the intervals in ascending order. This is an array giving the subintervals over which integration will occur. 'level' A rank-1 integer array of length M (=limit), containing the subdivision levels of the subintervals, i.e., if (aa,bb) is a subinterval of ``(pts[1], pts[2])`` where ``pts[0]`` and ``pts[2]`` are adjacent elements of ``infodict['pts']``, then (aa,bb) has level l if ``|bb-aa| = |pts[2]-pts[1]| * 2**(-l)``. 'ndin' A rank-1 integer array of length P+2. After the first integration over the intervals (pts[1], pts[2]), the error estimates over some of the intervals may have been increased artificially in order to put their subdivision forward. This array has ones in slots corresponding to the subintervals for which this happens. **Weighting the integrand** The input variables, *weight* and *wvar*, are used to weight the integrand by a select list of functions. Different integration methods are used to compute the integral with these weighting functions, and these do not support specifying break points. The possible values of weight and the corresponding weighting functions are. ========== =================================== ===================== ``weight`` Weight function used ``wvar`` ========== =================================== ===================== 'cos' cos(w*x) wvar = w 'sin' sin(w*x) wvar = w 'alg' g(x) = ((x-a)**alpha)*((b-x)**beta) wvar = (alpha, beta) 'alg-loga' g(x)*log(x-a) wvar = (alpha, beta) 'alg-logb' g(x)*log(b-x) wvar = (alpha, beta) 'alg-log' g(x)*log(x-a)*log(b-x) wvar = (alpha, beta) 'cauchy' 1/(x-c) wvar = c ========== =================================== ===================== wvar holds the parameter w, (alpha, beta), or c depending on the weight selected. In these expressions, a and b are the integration limits. For the 'cos' and 'sin' weighting, additional inputs and outputs are available. For weighted integrals with finite integration limits, the integration is performed using a Clenshaw-Curtis method, which uses Chebyshev moments. For repeated calculations, these moments are saved in the output dictionary: 'momcom' The maximum level of Chebyshev moments that have been computed, i.e., if ``M_c`` is ``infodict['momcom']`` then the moments have been computed for intervals of length ``|b-a| * 2**(-l)``, ``l=0,1,...,M_c``. 'nnlog' A rank-1 integer array of length M(=limit), containing the subdivision levels of the subintervals, i.e., an element of this array is equal to l if the corresponding subinterval is ``|b-a|* 2**(-l)``. 'chebmo' A rank-2 array of shape (25, maxp1) containing the computed Chebyshev moments. These can be passed on to an integration over the same interval by passing this array as the second element of the sequence wopts and passing infodict['momcom'] as the first element. If one of the integration limits is infinite, then a Fourier integral is computed (assuming w neq 0). If full_output is 1 and a numerical error is encountered, besides the error message attached to the output tuple, a dictionary is also appended to the output tuple which translates the error codes in the array ``info['ierlst']`` to English messages. The output information dictionary contains the following entries instead of 'last', 'alist', 'blist', 'rlist', and 'elist': 'lst' The number of subintervals needed for the integration (call it ``K_f``). 'rslst' A rank-1 array of length M_f=limlst, whose first ``K_f`` elements contain the integral contribution over the interval ``(a+(k-1)c, a+kc)`` where ``c = (2*floor(|w|) + 1) * pi / |w|`` and ``k=1,2,...,K_f``. 'erlst' A rank-1 array of length ``M_f`` containing the error estimate corresponding to the interval in the same position in ``infodict['rslist']``. 'ierlst' A rank-1 integer array of length ``M_f`` containing an error flag corresponding to the interval in the same position in ``infodict['rslist']``. See the explanation dictionary (last entry in the output tuple) for the meaning of the codes. **Details of QUADPACK level routines** `quad` calls routines from the FORTRAN library QUADPACK. This section provides details on the conditions for each routine to be called and a short description of each routine. The routine called depends on `weight`, `points` and the integration limits `a` and `b`. ================ ============== ========== ===================== QUADPACK routine `weight` `points` infinite bounds ================ ============== ========== ===================== qagse None No No qagie None No Yes qagpe None Yes No qawoe 'sin', 'cos' No No qawfe 'sin', 'cos' No either `a` or `b` qawse 'alg*' No No qawce 'cauchy' No No ================ ============== ========== ===================== The following provides a short description from [1]_ for each routine. qagse is an integrator based on globally adaptive interval subdivision in connection with extrapolation, which will eliminate the effects of integrand singularities of several types. The integration is performed using a 21-point Gauss-Kronrod quadrature within each subinterval. qagie handles integration over infinite intervals. The infinite range is mapped onto a finite interval and subsequently the same strategy as in ``QAGS`` is applied. qagpe serves the same purposes as QAGS, but also allows the user to provide explicit information about the location and type of trouble-spots i.e. the abscissae of internal singularities, discontinuities and other difficulties of the integrand function. qawoe is an integrator for the evaluation of :math:`\int^b_a \cos(\omega x)f(x)dx` or :math:`\int^b_a \sin(\omega x)f(x)dx` over a finite interval [a,b], where :math:`\omega` and :math:`f` are specified by the user. The rule evaluation component is based on the modified Clenshaw-Curtis technique An adaptive subdivision scheme is used in connection with an extrapolation procedure, which is a modification of that in ``QAGS`` and allows the algorithm to deal with singularities in :math:`f(x)`. qawfe calculates the Fourier transform :math:`\int^\infty_a \cos(\omega x)f(x)dx` or :math:`\int^\infty_a \sin(\omega x)f(x)dx` for user-provided :math:`\omega` and :math:`f`. The procedure of ``QAWO`` is applied on successive finite intervals, and convergence acceleration by means of the :math:`\varepsilon`-algorithm is applied to the series of integral approximations. qawse approximate :math:`\int^b_a w(x)f(x)dx`, with :math:`a < b` where :math:`w(x) = (x-a)^{\alpha}(b-x)^{\beta}v(x)` with :math:`\alpha,\beta > -1`, where :math:`v(x)` may be one of the following functions: :math:`1`, :math:`\log(x-a)`, :math:`\log(b-x)`, :math:`\log(x-a)\log(b-x)`. The user specifies :math:`\alpha`, :math:`\beta` and the type of the function :math:`v`. A globally adaptive subdivision strategy is applied, with modified Clenshaw-Curtis integration on those subintervals which contain `a` or `b`. qawce compute :math:`\int^b_a f(x) / (x-c)dx` where the integral must be interpreted as a Cauchy principal value integral, for user specified :math:`c` and :math:`f`. The strategy is globally adaptive. Modified Clenshaw-Curtis integration is used on those intervals containing the point :math:`x = c`. **Integration of Complex Function of a Real Variable** A complex valued function, :math:`f`, of a real variable can be written as :math:`f = g + ih`. Similarly, the integral of :math:`f` can be written as .. math:: \int_a^b f(x) dx = \int_a^b g(x) dx + i\int_a^b h(x) dx assuming that the integrals of :math:`g` and :math:`h` exist over the interval :math:`[a,b]` [2]_. Therefore, ``quad`` integrates complex-valued functions by integrating the real and imaginary components separately. **Array API Standard Support** `quad` has experimental support for Python Array API Standard compatible backends in addition to NumPy. Please consider testing these features by setting an environment variable ``SCIPY_ARRAY_API=1`` and providing CuPy, PyTorch, JAX, or Dask arrays as array arguments. The following combinations of backend and device (or other capability) are supported. ==================== ==================== ==================== Library CPU GPU ==================== ==================== ==================== NumPy ✅ n/a CuPy n/a ⛔ PyTorch ⛔ ⛔ JAX ⛔ ⛔ Dask ⛔ n/a ==================== ==================== ==================== See :ref:`dev-arrayapi` for more information. References ---------- .. [1] Piessens, Robert; de Doncker-Kapenga, Elise; Überhuber, Christoph W.; Kahaner, David (1983). QUADPACK: A subroutine package for automatic integration. Springer-Verlag. ISBN 978-3-540-12553-2. .. [2] McCullough, Thomas; Phillips, Keith (1973). Foundations of Analysis in the Complex Plane. Holt Rinehart Winston. ISBN 0-03-086370-8 Examples -------- Calculate :math:`\int^4_0 x^2 dx` and compare with an analytic result >>> from scipy import integrate >>> import numpy as np >>> x2 = lambda x: x**2 >>> integrate.quad(x2, 0, 4) (21.333333333333332, 2.3684757858670003e-13) >>> print(4**3 / 3.) # analytical result 21.3333333333 Calculate :math:`\int^\infty_0 e^{-x} dx` >>> invexp = lambda x: np.exp(-x) >>> integrate.quad(invexp, 0, np.inf) (1.0, 5.842605999138044e-11) Calculate :math:`\int^1_0 a x \,dx` for :math:`a = 1, 3` >>> f = lambda x, a: a*x >>> y, err = integrate.quad(f, 0, 1, args=(1,)) >>> y 0.5 >>> y, err = integrate.quad(f, 0, 1, args=(3,)) >>> y 1.5 Calculate :math:`\int^1_0 x^2 + y^2 dx` with ctypes, holding y parameter as 1:: testlib.c => double func(int n, double args[n]){ return args[0]*args[0] + args[1]*args[1];} compile to library testlib.* :: from scipy import integrate import ctypes lib = ctypes.CDLL('/home/.../testlib.*') #use absolute path lib.func.restype = ctypes.c_double lib.func.argtypes = (ctypes.c_int,ctypes.c_double) integrate.quad(lib.func,0,1,(1)) #(1.3333333333333333, 1.4802973661668752e-14) print((1.0**3/3.0 + 1.0) - (0.0**3/3.0 + 0.0)) #Analytic result # 1.3333333333333333 Be aware that pulse shapes and other sharp features as compared to the size of the integration interval may not be integrated correctly using this method. A simplified example of this limitation is integrating a y-axis reflected step function with many zero values within the integrals bounds. >>> y = lambda x: 1 if x<=0 else 0 >>> integrate.quad(y, -1, 1) (1.0, 1.1102230246251565e-14) >>> integrate.quad(y, -1, 100) (1.0000000002199108, 1.0189464580163188e-08) >>> integrate.quad(y, -1, 10000) (0.0, 0.0) File: ~/miniconda3-clean/envs/work311/lib/python3.11/site-packages/scipy/integrate/_quadpack_py.py Type: function
# Find selected NumPy names rather than printing the entire module directory.
[name for name in dir(np) if name.startswith("log")]
['log', 'log10', 'log1p', 'log2', 'logaddexp', 'logaddexp2', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'logspace']
For example, scipy.integrate.quad returns both the numerical integral and an estimate of its absolute error:
value, estimated_error = integrate.quad(lambda x: np.exp(-x), 0, np.inf)
print("integral =", value)
print("estimated absolute error =", estimated_error)
integral = 1.0 estimated absolute error = 5.842605965544164e-11
Figures appear inline¶
Matplotlib figures produced by a code cell are displayed as part of the notebook output.
import matplotlib.pyplot as plt
x = np.linspace(0, 2*np.pi, 300)
plt.plot(x, np.sin(x), label=r"$\sin x$")
plt.plot(x, np.cos(x), label=r"$\cos x$")
plt.xlabel(r"$x$")
plt.ylabel("function value")
plt.legend()
plt.show()
IPython commands: magics and the system shell¶
A Python kernel in Jupyter normally runs through IPython. IPython adds convenient syntax that is not part of the Python language:
- line magics begin with
%, for example%timeitand%pwd; - cell magics begin with
%%and apply to an entire cell; !commandsends a command to the system shell.
Shell commands are operating-system dependent and can make a notebook less portable.
%pwd
'/Users/haule/Teaching/ComputationalPhysics/2026/Programing/src'
%timeit np.sin(x)
906 ns ± 4.95 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
# These are shell commands, not Python statements.
!pwd
!ls
/Users/haule/Teaching/ComputationalPhysics/2026/Programing/src __pycache__ 00_Introduction_before_revision_2026-09-18.ipynb 00_Introduction.ipynb 01_Basic_Python_with_solution.ipynb 01_Basic_Python_with_solution2.ipynb 01_Basic_Python.html 01_Basic_Python.ipynb 02_Numpy_with_solution.ipynb 02_Numpy.html 02_Numpy.ipynb 03_Scipy_old.ipynb 03_Scipy.html 03_Scipy.ipynb 04_Scipy_Hydrogen_atom_with_solution.ipynb 04_Scipy_Hydrogen_atom.ipynb 05_Atom_in_LDA_.html 05_Atom_in_LDA_.ipynb 05_Atom_in_LDA.html 05_Atom_in_LDA.ipynb 06_ODE_solution.ipynb 06_ODE.html 06_ODE.ipynb anaconda_projects backup double_pendulum.mp4 excor.py img my_out.txt mymodule.py old old2 pendulum.py ST_data.npy stockholm_daily_mean_temperature.csv stockholm_td_adj.dat stockholm_td_adj.dat.txt StockholmT.dat tmp
Which Python environment is the notebook using?¶
The notebook kernel—not the terminal from which Jupyter was launched—determines which Python interpreter and packages execute the code. The kernel name is displayed near the upper-right corner of the notebook interface.
sys.executable gives the interpreter used by the active Python kernel:
import sys
print(sys.executable)
print("Python", sys.version.split()[0])
print("NumPy", np.__version__)
!which python
/Users/haule/miniconda3-clean/envs/work311/bin/python Python 3.11.14 NumPy 2.3.5 /Users/haule/miniconda3-clean/envs/work311/bin/python
By contrast, !which python only reports which executable the shell finds through its PATH; it can differ from the active kernel.
When a package must be installed from a notebook, prefer IPython's %pip magic:
%pip install package_name
It targets the current kernel environment more reliably than !pip install .... A kernel restart may be necessary after installing or updating a package. For this course, install the required packages in advance rather than modifying the environment during every notebook run.
Files and working directories¶
Relative paths are interpreted from the kernel's current working directory, which may not be the directory you expected. Inspect it explicitly when reading data files:
from pathlib import Path
working_directory = Path.cwd()
print(working_directory)
print("Notebook files here:", [p.name for p in working_directory.glob("*.ipynb")])
/Users/haule/Teaching/ComputationalPhysics/2026/Programing/src Notebook files here: ['00_Introduction.ipynb', '03_Scipy_old.ipynb', '01_Basic_Python_with_solution.ipynb', '04_Scipy_Hydrogen_atom.ipynb', '00_Introduction_before_revision_2026-09-18.ipynb', '01_Basic_Python.ipynb', '06_ODE.ipynb', '02_Numpy_with_solution.ipynb', '06_ODE_solution.ipynb', '02_Numpy.ipynb', '05_Atom_in_LDA.ipynb', '05_Atom_in_LDA_.ipynb', '03_Scipy.ipynb', '04_Scipy_Hydrogen_atom_with_solution.ipynb', '01_Basic_Python_with_solution2.ipynb']
Prefer pathlib.Path over manually concatenating directory strings:
data_file = Path("data") / "measurement.csv"
Do not hard-code a personal absolute path when the notebook will be shared with other people.
Saving, outputs, and reproducibility¶
An .ipynb file is a JSON document containing cell sources, metadata, and any outputs that were saved. Saved output is only a snapshot; it does not prove that the current code produced it.
Before sharing a notebook:
- Save a backup if it contains important work.
- Restart the kernel and run all cells from top to bottom.
- Check that there are no errors or hidden dependencies on execution order.
- Remove large, obsolete, private, or machine-specific outputs.
- Use relative paths and state the packages or environment required.
If a file changes on disk while it is open in the browser, reload it before saving. Otherwise, the older browser copy may overwrite the newer file.
Sharing notebooks¶
A saved .ipynb file can be sent directly to another person. Common alternatives are:
- export a static HTML copy with
jupyter nbconvert --to html notebook.ipynb; - render a public notebook statically on GitHub or nbviewer;
- provide an executable environment through Binder;
- use JupyterHub to provide managed notebook environments to a class.
Static HTML, GitHub, and nbviewer displays do not execute the Python cells. Reproducing the calculation requires both the notebook and a compatible Python environment.
Notebook security¶
A notebook can contain arbitrary Python and shell commands. Read unfamiliar notebooks before executing them, especially cells that access files, install software, use credentials, or communicate over the network.
Jupyter distinguishes trusted and untrusted notebook output. Trusting a notebook affects whether previously saved HTML and JavaScript output is displayed; it does not make arbitrary code safe to run.
Finishing a session¶
Saving the notebook and stopping the kernel are different actions:
- Save writes the notebook document and its current outputs to disk.
- Restart Kernel clears all Python variables and imports but keeps the notebook open.
- Interrupt Kernel stops a calculation that is currently running.
- Shut Down Kernel/Notebook terminates the Python process.
- Closing the browser tab alone may leave the kernel running.
For a final check, save the notebook, restart the kernel, run all cells, and confirm that the results can be reproduced in order.