Skip to article frontmatterSkip to article content

Abstract

Functions allow programmers to reuse code that is efficiently implemented and well-tested. This notebook not only demonstrates the basic syntax for using and writing functions, but also uses concrete examples to illustrate the importance of code reuse. It highlights the flexibility of functions by showing how they can be applied with different arguments to solve similar problems and how they can be customized to suit specific applications.

import math
import ROOT

%reload_ext divewidgets
if not input('Load JupyterAI? [Y/n]').lower()=='n':
    %reload_ext jupyter_ai
Load JupyterAI? [Y/n] 

What is a Function?

A function is a callable object, e.g.:

callable(callable), callable(1)
(True, False)

The function callable is callable in the sense that

  • it can be called/invoked with some input arguments/parameters such as 1 enclosed by parentheses (), and then
  • returns some value computed from the input arguments, such as the boolean value False to indicate that the input argument 1 is not callable.

A function can be defined using the def keyword.

E.g., a simple function that prints “Hello, World!” can be defined as follows:

# Function definition
def say_hello():
    print("Hello, World!")
# Function invocation
say_hello()
Hello, World!

To make a function more powerful and solve different problems,

def increment(x):
    return x + 1


increment(3)
4

A function must have a return value. By default None is returned.

print(f"The return value is {say_hello()}.", )
Hello, World!
The return value is None.

We can also have multiple input arguments.

def length_of_hypotenuse(a, b):
    return (a ** 2 + b ** 2) ** 0.5

length_of_hypotenuse(1, 2), length_of_hypotenuse(3, 4)
(2.23606797749979, 5.0)

The arguments are evaluated from left to right:

print("1st input:", input(), "\n2nd input:", input())
 1
 2
1st input: 1 
2nd input: 2

Indeed, how arguments are passed into a function can be more complicated than you may think. To check if you have the correct understanding:

%%optlite -l -h 400
def increment(x):
    x += 1


x = 3
increment(x)
print(x)  # 4?
Loading...
%%cpp
void increment(auto &x) {
    x += 1;
}

auto x = 3;
increment(x);
x
(int) 4
%%ai
Explain briefly the differences in how Python, C, and Java pass arguments to 
functions. In particular, explain
1. call by value,
2. call by reference, and
3. call by object reference.
Loading...

A fundamental property of functions in Python is that they are first-class citizens, which means that a function can be

  1. assigned to a variable,
  2. passed as an input argument, and
  3. returned by a function.
%%ai
Are there programming languages that do not treat functions as first-class citizens? Why?
Loading...

The following is a simple illustration using the def statement to define an identity function that uses the return statement to return the input argument.

%%optlite -h 300
def i(x):
    return x
assert i(i) == i and i.__name__ == 'i'
Loading...

A function can also be defined using the lambda expression, which creates an anonymous function:[1]

%%optlite -h 300
assert (i := lambda x: x)(i) == i \
and i.__name__ == "<lambda>"
Loading...

A non-trivial example is the following implementation of the boolean values as functions:

%%optlite -h 450
def true(x, y): return x
def false(x, y): return y
def ifthenelse(b, x, y): return b(x, y)
assert ifthenelse(true, "A", "B") == "A"
assert ifthenelse(false, "A", "B") == "B"
Loading...
%%ai
How to create an infinite loop using only the lambda expression in Python?
Loading...

Perhaps you may also be interested in the following:

%%ai
Why Alonzo Church used lambda for lambda calculus?
Loading...

Code Reuse

Previously, we learned about iteration, where the same piece of code can run multiple times. Function abstraction take this even further: It allows the same piece of code to be executed with different parameters and at different locations. Code reuse is a good programming practice. If done properly, it makes the code readible and efficient. We will explore these benefits using a concrete example below.

Perfect Square

For instance, the first 10 perfect squares are:

for i in range(10):
    print(i**2)
0
1
4
9
16
25
36
49
64
81

Instead of generating perfect squares, how about writing a function that checks if a number is a perfect square?

def is_perfect_square(n):
    ### BEGIN SOLUTION
    return n == math.isqrt(n) ** 2
    ### END SOLUTION
# test cases
assert is_perfect_square(10**2)
assert not is_perfect_square(10**2 + 1)
assert is_perfect_square(10**10)
assert not is_perfect_square(10**10 + 1)
assert is_perfect_square(10**100)
assert not is_perfect_square(10**100 + 1)

As another demonstration of code reuse, the following solution uses a for loop to implement Definition 1 exactly.

def is_perfect_square(n):
    # checks if n is the square of i for i in the range up to n (exclusive). 
    for i in range(n):
        if i**2 == n:
            return True

If you try running the test on the above solution, it will take an unacceptably long time to run.[2] (Why?)

To properly test the function, we should modify it to fail if it takes too long to run. Implementing such a feature, called timeout, is difficult. Fortunately, we can reuse the code written by others. Run the following cell to

  1. install the package wrapt_timeout_decorator and
  2. import the function timeout from the module wrapt_timeout_decorator.
%pip install wrapt_timeout_decorator >/dev/null 2>&1
from wrapt_timeout_decorator import timeout
Note: you may need to restart the kernel to use updated packages.

You will learn how to import a function in a subsequent section (Importing External Modules). For now, let’s see how to use the timeout function:

# enhanced test without timeout
duration = 5


@timeout(duration)  # raise error if the test does not complete in 5 seconds.
def test():
    if not input(f"Run the test with a timeout of {duration}s? [Y/n]").lower() == "n":
        assert is_perfect_square(10**2)
        assert not is_perfect_square(10**2 + 1)
        assert is_perfect_square(10**10)
        assert not is_perfect_square(10**10 + 1)
        assert is_perfect_square(10**100)
        assert not is_perfect_square(10**100 + 1)


test()  # run the test
Run the test with a timeout of 5s? [Y/n] 
---------------------------------------------------------------------------
TimeoutError                              Traceback (most recent call last)
Cell In[19], line 16
     12         assert is_perfect_square(10**100)
     13         assert not is_perfect_square(10**100 + 1)
---> 16 test()  # run the test

File /opt/conda/lib/python3.12/site-packages/wrapt_timeout_decorator/wrapt_timeout_decorator.py:229, in timeout.<locals>.wrapper(wrapped, instance, args, kwargs)
    227     return wrapped(*wrap_helper.args, **wrap_helper.kwargs)
    228 else:
--> 229     return wrapped_with_timeout(wrap_helper)

File /opt/conda/lib/python3.12/site-packages/wrapt_timeout_decorator/wrapt_timeout_decorator.py:236, in wrapped_with_timeout(wrap_helper)
    234 def wrapped_with_timeout(wrap_helper: WrapHelper) -> Any:
    235     if wrap_helper.use_signals:
--> 236         return wrapped_with_timeout_signals(wrap_helper)
    237     else:
    238         return wrapped_with_timeout_process(wrap_helper)

File /opt/conda/lib/python3.12/site-packages/wrapt_timeout_decorator/wrapt_timeout_decorator.py:244, in wrapped_with_timeout_signals(wrap_helper)
    242 try:
    243     wrap_helper.save_old_and_set_new_alarm_handler()
--> 244     return wrap_helper.wrapped(*wrap_helper.args, **wrap_helper.kwargs)
    245 finally:
    246     wrap_helper.restore_old_alarm_handler()

Cell In[19], line 11, in test()
      9 assert not is_perfect_square(10**2 + 1)
     10 assert is_perfect_square(10**10)
---> 11 assert not is_perfect_square(10**10 + 1)
     12 assert is_perfect_square(10**100)
     13 assert not is_perfect_square(10**100 + 1)

Cell In[18], line 4, in is_perfect_square(n)
      1 def is_perfect_square(n):
      2     # checks if n is the square of i for i in the range up to n (exclusive). 
      3     for i in range(n):
----> 4         if i**2 == n:
      5             return True

File /opt/conda/lib/python3.12/site-packages/wrapt_timeout_decorator/wrap_helper.py:97, in WrapHelper.new_alarm_handler(self, signum, frame)
     96 def new_alarm_handler(self, signum: signal.Signals, frame: FrameType) -> None:
---> 97     raise_exception(self.timeout_exception, self.exception_message)

File /opt/conda/lib/python3.12/site-packages/wrapt_timeout_decorator/wrap_helper.py:249, in raise_exception(exception, exception_message)
    247 if not exception:
    248     exception = TimeoutError
--> 249 raise exception(exception_message)

TimeoutError: Function test timed out after 5.0 seconds

To add timeout to the test, we simply

  1. wrapped the test inside a function test, and
  2. decorated it with @timeout(duration).

The function test will then be capabable of raising a TimeOutError if it takes more than the specified time duration to run.

You will learn how to write a decorator later. It is a powerful way to reuse functions and other objects with additional customizations.

Integer Square Root

To improve the efficiency, consider the following sufficient and necessary condition for perfect squares:

A simple implementation is as follows:

def is_perfect_square(n):
    # check if n is the square of its integer square root
    return n == int(n**0.5) ** 2

assert is_perfect_square(10**10)

Note that it fixed the efficiency issue on the test case with n being 10**10. Let’s run all the test cases:

test()
Run the test with a timeout of 5s? [Y/n] 
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[21], line 1
----> 1 test()

File /opt/conda/lib/python3.12/site-packages/wrapt_timeout_decorator/wrapt_timeout_decorator.py:229, in timeout.<locals>.wrapper(wrapped, instance, args, kwargs)
    227     return wrapped(*wrap_helper.args, **wrap_helper.kwargs)
    228 else:
--> 229     return wrapped_with_timeout(wrap_helper)

File /opt/conda/lib/python3.12/site-packages/wrapt_timeout_decorator/wrapt_timeout_decorator.py:236, in wrapped_with_timeout(wrap_helper)
    234 def wrapped_with_timeout(wrap_helper: WrapHelper) -> Any:
    235     if wrap_helper.use_signals:
--> 236         return wrapped_with_timeout_signals(wrap_helper)
    237     else:
    238         return wrapped_with_timeout_process(wrap_helper)

File /opt/conda/lib/python3.12/site-packages/wrapt_timeout_decorator/wrapt_timeout_decorator.py:244, in wrapped_with_timeout_signals(wrap_helper)
    242 try:
    243     wrap_helper.save_old_and_set_new_alarm_handler()
--> 244     return wrap_helper.wrapped(*wrap_helper.args, **wrap_helper.kwargs)
    245 finally:
    246     wrap_helper.restore_old_alarm_handler()

Cell In[19], line 12, in test()
     10 assert is_perfect_square(10**10)
     11 assert not is_perfect_square(10**10 + 1)
---> 12 assert is_perfect_square(10**100)
     13 assert not is_perfect_square(10**100 + 1)

AssertionError: 

Perhaps we should use math.isclose instead of ==, since int(n**0.5) ** 2 is a float.

def is_perfect_square(n):
    return math.isclose(n, int(n**0.5) ** 2)


assert is_perfect_square(10**100)

Note that it can correctly say 10**100 is a perfect square. Let’s run all the test cases:

test()
Run the test with a timeout of 5s? [Y/n] 
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[23], line 1
----> 1 test()

File /opt/conda/lib/python3.12/site-packages/wrapt_timeout_decorator/wrapt_timeout_decorator.py:229, in timeout.<locals>.wrapper(wrapped, instance, args, kwargs)
    227     return wrapped(*wrap_helper.args, **wrap_helper.kwargs)
    228 else:
--> 229     return wrapped_with_timeout(wrap_helper)

File /opt/conda/lib/python3.12/site-packages/wrapt_timeout_decorator/wrapt_timeout_decorator.py:236, in wrapped_with_timeout(wrap_helper)
    234 def wrapped_with_timeout(wrap_helper: WrapHelper) -> Any:
    235     if wrap_helper.use_signals:
--> 236         return wrapped_with_timeout_signals(wrap_helper)
    237     else:
    238         return wrapped_with_timeout_process(wrap_helper)

File /opt/conda/lib/python3.12/site-packages/wrapt_timeout_decorator/wrapt_timeout_decorator.py:244, in wrapped_with_timeout_signals(wrap_helper)
    242 try:
    243     wrap_helper.save_old_and_set_new_alarm_handler()
--> 244     return wrap_helper.wrapped(*wrap_helper.args, **wrap_helper.kwargs)
    245 finally:
    246     wrap_helper.restore_old_alarm_handler()

Cell In[19], line 11, in test()
      9 assert not is_perfect_square(10**2 + 1)
     10 assert is_perfect_square(10**10)
---> 11 assert not is_perfect_square(10**10 + 1)
     12 assert is_perfect_square(10**100)
     13 assert not is_perfect_square(10**100 + 1)

AssertionError: 

How to fix the issue? The culprit is that the computation for integer square root is not exact:

x = 10**100
int((x) ** 0.5)
100000000000000007629769841091887003294964970946560

There are better ways to compute integer square root. Binary search is a relatively easy one to try first, although it is not the best choice.

%%ai
Explain very briefly how integer square root can be implemented in Python using
binary search.
Loading...

But there is a much easier way to have a better implementation: Code reuse! Try math.isqrt for Exercise 1 and check that you can pass all the test cases instantly.

x = 10**100
math.isqrt(x), int((x) ** 0.5)
(100000000000000000000000000000000000000000000000000, 100000000000000007629769841091887003294964970946560)
%%ai
Explain briefly in two paragraph how isqrt is implemented as an 
adaptive-precision pure-integer version of Newton's iteration.
Loading...

While you may want to write self-contained codes that do not rely on external libraries, code reuse advocates would recommend you to use standard libraries as much as possible. Why?

Indeed, the math library provides functions that it does not implement:

CPython implementation detail: The math module consists mostly of thin wrappers around the platform C math library functions. - pydoc last paragraph

E.g., see the source code wrapper for log.[3]:

%%ai
When working on a programming assignment, should I write all the code myself,
or is it acceptable to use standard libraries?

Modules

To facilitate code reuse, all Python codes are organized into libraries called modules. E.g., you can list all available modules using pip list:

%pip list
Package                         Version
------------------------------- --------------
accelerate                      1.10.1
aiohappyeyeballs                2.6.1
aiohttp                         3.12.15
aiosignal                       1.4.0
alabaster                       1.0.0
alembic                         1.16.4
altair                          5.5.0
annotated-types                 0.7.0
anyio                           4.10.0
argon2-cffi                     25.1.0
argon2-cffi-bindings            25.1.0
arrow                           1.3.0
astroid                         3.3.11
asttokens                       3.0.0
astunparse                      1.6.3
astyle                          3.6.9
async_generator                 1.10
async-lru                       2.0.5
async-timeout                   4.0.3
attrs                           25.3.0
autopep8                        2.3.2
av                              13.1.0
babel                           2.17.0
backports.tarfile               1.2.0
beautifulsoup4                  4.13.5
bitsandbytes                    0.47.0
black                           25.1.0
bleach                          6.2.0
blessed                         1.21.0
blinker                         1.9.0
bokeh                           3.7.3
Bottleneck                      1.5.0
Brotli                          1.1.0
build                           1.3.0
CacheControl                    0.14.3
cached-property                 1.5.2
calysto-scheme                  1.4.8
certifi                         2025.8.3
certipy                         0.2.2
cffi                            1.17.1
charset-normalizer              3.4.3
cleo                            2.1.0
cli_exit_tools                  1.2.7
click                           8.2.1
click-default-group             1.2.4
cloudpickle                     3.1.1
cloup                           3.0.8
colorama                        0.4.6
comm                            0.2.3
contourpy                       1.3.3
crashtest                       0.4.1
cryptography                    45.0.6
cycler                          0.12.1
Cython                          3.1.3
cytoolz                         1.0.1
dask                            2025.7.0
dataclasses                     0.8
dataclasses-json                0.6.7
datasets                        4.0.0
debugpy                         1.8.16
decorator                       5.2.1
deepmerge                       2.0
defusedxml                      0.7.1
device-smi                      0.4.1
dill                            0.3.8
distlib                         0.4.0
distributed                     2025.7.0
distro                          1.9.0
dive_ai                         0.0.1
divewidgets                     0.1.5
docstring-to-markdown           0.17
docutils                        0.21.2
doit                            0.36.0
duckdb                          1.3.2+g2063dda
dulwich                         0.22.8
exceptiongroup                  1.3.0
execnb                          0.1.14
executing                       2.2.0
faiss                           1.9.0
faiss-cpu                       1.12.0
fastapi                         0.116.1
fastcore                        1.8.8
fastjsonschema                  2.21.2
filelock                        3.19.1
findpython                      0.6.3
flake8                          7.1.2
flashinfer-python               0.2.5
fonttools                       4.59.1
fqdn                            1.5.1
frozenlist                      1.7.0
fsspec                          2025.7.0
ghapi                           1.0.6
ghp-import                      2.1.0
gitdb                           4.0.12
GitPython                       3.1.45
glcontext                       3.0.0
gmpy2                           2.2.1
gptqmodel                       2.2.0
gpustat                         1.1.1
greenlet                        3.2.4
h11                             0.16.0
h2                              4.2.0
h5py                            3.14.0
hf_transfer                     0.1.9
hf-xet                          1.1.8
hpack                           4.1.0
httpcore                        1.0.9
httpx                           0.28.1
httpx-sse                       0.4.1
huggingface-hub                 0.34.4
hyperframe                      6.1.0
idna                            3.10
imagesize                       1.4.1
importlib_metadata              8.7.0
importlib_resources             6.5.2
iniconfig                       2.1.0
installer                       0.7.0
ipykernel                       6.29.5
ipympl                          0.9.7
ipyparallel                     9.0.1
ipython                         9.4.0
ipython_pygments_lexers         1.1.1
ipywidgets                      8.1.7
isoduration                     20.11.0
isort                           6.0.1
isosurfaces                     0.1.2
itsdangerous                    2.2.0
jaraco.classes                  3.4.0
jaraco.context                  6.0.1
jaraco.functools                4.3.0
jdc                             0.0.9
jedi                            0.19.2
jeepney                         0.9.0
Jinja2                          3.1.6
jiter                           0.10.0
joblib                          1.5.1
json5                           0.12.1
jsonpatch                       1.33
jsonpath-ng                     1.7.0
jsonpointer                     3.0.0
jsonschema                      4.25.1
jsonschema-specifications       2025.4.1
jupyter_ai                      2.31.6
jupyter_ai_magics               2.31.6
jupyter-archive                 3.4.0
jupyter_client                  8.6.3
jupyter_core                    5.8.1
jupyter-events                  0.12.0
jupyter-lsp                     2.2.6
jupyter-marimo-proxy            0.0.4
jupyter-remote-desktop-proxy    3.0.1
jupyter-resource-usage          1.1.1
jupyter_server                  2.17.0
jupyter_server_mathjax          0.2.6
jupyter_server_proxy            4.4.0
jupyter_server_terminals        0.5.3
jupyter_vscode_proxy            0.0.0
jupyter-www-proxy               0.1.2
jupyterhub                      5.3.0
jupyterlab                      4.4.6
jupyterlab_code_formatter       3.0.2
jupyterlab-deck                 0.2.0
jupyterlab_execute_time         3.2.0
jupyterlab_fonts                3.0.0
jupyterlab_git                  0.51.2
jupyterlab-lsp                  5.1.1
jupyterlab_myst                 2.4.2
jupyterlab_pygments             0.3.0
jupyterlab-quarto               0.3.5
jupyterlab_server               2.27.3
jupyterlab_theme_solarized_dark 3.0.1
jupyterlab_widgets              3.0.15
jupyterlite                     0.6.4
jupyterlite-core                0.6.4
jupyterlite-sphinx              0.20.2
jupytext                        1.17.2
jwcrypto                        1.5.6
kaleido                         0.2.1
keyring                         25.6.0
kiwisolver                      1.4.9
langchain                       0.3.27
langchain-community             0.3.27
langchain-core                  0.3.74
langchain-ollama                0.3.7
langchain-openai                0.3.31
langchain-text-splitters        0.3.9
langsmith                       0.3.45
lark                            1.2.2
lib-detect-testenv              2.0.8
llvmlite                        0.44.0
locket                          1.0.0
logbar                          0.0.4
loro                            1.5.4
Mako                            1.3.10
manim                           0.18.1
ManimPango                      0.6.0
mapbox_earcut                   1.0.3
marimo                          0.15.0
Markdown                        3.8.2
markdown-it-py                  4.0.0
MarkupSafe                      3.0.2
marshmallow                     3.26.1
matplotlib                      3.10.5
matplotlib-inline               0.1.7
mccabe                          0.7.0
mdit-py-plugins                 0.5.0
mdurl                           0.1.2
metakernel                      0.30.3
mistune                         3.1.3
ml_dtypes                       0.5.3
mlc-ai-nightly-cu128            0.20.dev427
mlc-llm-nightly-cu128           0.20.dev59
moderngl                        5.11.1
moderngl-window                 3.1.1
more-itertools                  10.7.0
mpmath                          1.3.0
msgpack                         1.1.1
multidict                       6.6.3
multiprocess                    0.70.16
munkres                         1.1.4
mypy_extensions                 1.1.0
narwhals                        2.2.0
nbclient                        0.10.2
nbconvert                       7.16.6
nbdev                           2.4.5
nbdime                          4.0.2
nbformat                        5.10.4
nbgitpuller                     1.2.2
nbgrader                        0.9.5
nest_asyncio                    1.6.0
networkx                        3.5
ninja                           1.13.0
notebook                        7.4.5
notebook_shim                   0.2.4
numba                           0.61.2
numexpr                         2.10.2
numpy                           1.26.4
nvidia-cublas-cu12              12.8.4.1
nvidia-cuda-cupti-cu12          12.8.90
nvidia-cuda-nvrtc-cu12          12.8.93
nvidia-cuda-runtime-cu12        12.8.90
nvidia-cudnn-cu12               9.10.2.21
nvidia-cufft-cu12               11.3.3.83
nvidia-cufile-cu12              1.13.1.3
nvidia-curand-cu12              10.3.9.90
nvidia-cusolver-cu12            11.7.3.90
nvidia-cusparse-cu12            12.5.8.93
nvidia-cusparselt-cu12          0.7.1
nvidia-ml-py                    12.575.51
nvidia-nccl-cu12                2.27.3
nvidia-nvjitlink-cu12           12.8.93
nvidia-nvtx-cu12                12.8.90
nvitop                          1.5.3
oauthlib                        3.3.1
ollama                          0.5.3
openai                          1.101.0
optimum                         1.27.0
orjson                          3.11.2
overrides                       7.7.0
packaging                       24.2
pamela                          1.2.0
pandas                          2.2.3
pandocfilters                   1.5.0
parso                           0.8.5
partd                           1.4.2
pathspec                        0.12.1
patsy                           1.0.1
pbs-installer                   2025.8.18
pexpect                         4.9.0
pickleshare                     0.7.5
pillow                          11.3.0
pip                             25.2
pkginfo                         1.12.1.2
platformdirs                    4.3.8
plotly                          6.1.2
pluggy                          1.6.0
ply                             3.11
poetry                          2.1.2
poetry-core                     2.1.2
polars                          1.30.0
portalocker                     3.2.0
prometheus_client               0.22.1
prompt_toolkit                  3.0.51
propcache                       0.3.1
protobuf                        6.32.0
psutil                          5.9.8
ptyprocess                      0.7.0
pure_eval                       0.2.3
py-cpuinfo                      9.0.0
pyarrow                         20.0.0
pycairo                         1.28.0
pycodestyle                     2.12.1
pycparser                       2.22
pycurl                          7.45.6
pydantic                        2.11.7
pydantic_core                   2.33.2
pydantic-settings               2.10.1
pydocstyle                      6.3.0
pydub                           0.25.1
pyflakes                        3.2.0
pyglet                          2.1.8
pyglm                           2.8.2
Pygments                        2.19.2
PyJWT                           2.10.1
pylint                          3.3.8
pymdown-extensions              10.16.1
pyparsing                       3.2.3
PyPDF2                          3.0.1
pyproject_hooks                 1.2.0
PySocks                         1.7.1
pytest                          8.4.1
python-dateutil                 2.9.0.post0
python-dotenv                   1.1.1
python-json-logger              2.0.7
python-lsp-jsonrpc              1.1.2
python-lsp-server               1.12.2
pytoolconfig                    1.3.1
pytz                            2025.2
PyYAML                          6.0.2
pyzmq                           27.0.2
random_word                     1.0.13
RapidFuzz                       3.13.0
redis                           6.4.0
referencing                     0.36.2
regex                           2025.7.34
requests                        2.32.5
requests-toolbelt               1.0.0
rfc3339_validator               0.1.4
rfc3986-validator               0.1.1
rfc3987-syntax                  1.1.0
rich                            14.1.0
roman-numerals-py               3.1.0
rope                            1.14.0
rpds-py                         0.27.0
ruff                            0.12.10
safetensors                     0.6.2
scikit-learn                    1.7.1
scipy                           1.16.1
screeninfo                      0.8.1
seaborn                         0.13.2
SecretStorage                   3.3.3
Send2Trash                      1.8.3
sentence-transformers           5.1.0
sentencepiece                   0.2.1
setuptools                      80.9.0
shellingham                     1.5.4
shortuuid                       1.0.13
simpervisor                     1.0.0
six                             1.17.0
skia-pathops                    0.8.0.post2
smmap                           5.0.2
sniffio                         1.3.1
snowballstemmer                 3.0.1
sortedcontainers                2.4.0
soupsieve                       2.7
Sphinx                          8.3.0
sphinxcontrib-applehelp         2.0.0
sphinxcontrib-devhelp           2.0.0
sphinxcontrib-htmlhelp          2.1.0
sphinxcontrib-jsmath            1.0.1
sphinxcontrib-qthelp            2.0.0
sphinxcontrib-serializinghtml   1.1.10
SQLAlchemy                      2.0.43
sqlglot                         26.22.1
srt                             3.5.3
stack_data                      0.6.3
starlette                       0.47.3
statsmodels                     0.14.5
svgelements                     1.9.6
sympy                           1.14.0
tables                          3.10.2
tblib                           3.1.0
tenacity                        9.1.2
terminado                       0.18.1
theme-darcula                   4.0.0
threadpoolctl                   3.6.0
tiktoken                        0.11.0
tinycss2                        1.4.0
tokenicer                       0.0.4
tokenizers                      0.21.4
tomli                           2.2.1
tomlkit                         0.13.3
toolz                           1.0.0
torch                           2.8.0+cu128
torchaudio                      2.8.0+cu128
torchvision                     0.23.0+cu128
tornado                         6.5.2
tqdm                            4.67.1
traitlets                       5.14.3
transformers                    4.55.4
triton                          3.4.0
trove-classifiers               2025.8.6.13
types-python-dateutil           2.9.0.20250822
typing_extensions               4.14.1
typing_inspect                  0.9.0
typing-inspection               0.4.1
typing_utils                    0.1.0
tzdata                          2025.2
ujson                           5.11.0
unicodedata2                    16.0.0
uri-template                    1.3.0
urllib3                         2.5.0
uvicorn                         0.35.0
virtualenv                      20.34.0
watchdog                        6.0.0
wcwidth                         0.2.13
webcolors                       24.11.1
webencodings                    0.5.1
websocket-client                1.8.0
websockets                      15.0.1
websockify                      0.13.0
whatthepatch                    1.0.7
wheel                           0.45.1
widgetsnbextension              4.0.14
wrapt                           1.17.3
wrapt_timeout_decorator         1.5.1
xlrd                            2.0.2
xrootd                          5.8.4
xxhash                          3.5.0
xyzservices                     2025.4.0
yapf                            0.43.0
yarl                            1.20.1
yasi                            2.1.2
zict                            3.0.0
zipp                            3.23.0
zstandard                       0.23.0
Note: you may need to restart the kernel to use updated packages.

Python searches for packages using the search path:

import sys
sys.path
['/opt/conda/lib/python312.zip', '/opt/conda/lib/python3.12', '/opt/conda/lib/python3.12/lib-dynload', '', '/opt/conda/lib/python3.12/site-packages']

For instance, to show the location of a package, say divewidgets, run:

%pip show divewidgets
Name: divewidgets
Version: 0.1.5
Summary: Jupyter Widgets for DIVE virtual learning environment.
Home-page: https://github.com/dive4dec/divewidgets
Author: Chung Chan
Author-email: chungc@alum.mit.edu
License: BSD
Location: /opt/conda/lib/python3.12/site-packages
Requires: ipywidgets
Required-by: 
Note: you may need to restart the kernel to use updated packages.

Builtins Module

In Python, every function must come from a module, including the build-in functions:

__builtins__.print(f"`{print.__name__}` is from the {print.__module__} module.")
`print` is from the builtins module.

The buildins are automatically imported as __builtins__ (and also __builtin__) along with all the functions and objects it provides because they are commonly use by programmers.

We can use the built-in function dir (directory) to list all built-in objects available.

dir(__builtins__)

For instance, there is a built-in function help for showing the docstring (documentation string) of functions or other objects.

help(help)  # can also show the docstring of help itself
help(__builtins__)  # can also show the docstring of a module
print(dir())
Solution to Exercise 2

As summarized by the first line of the docstring of dir:

If called without an argument, return the names in the current scope.

Importing External Modules

For other available modules, we can use the import statement to import multiple functions or objects into the program global frame.

%%optlite -h 300
from math import ceil, log10

x = 1234
print("Number of digits of x:", ceil(log10(x)))
Loading...

The above imports both the functions log10 and ceil from math to compute the number log10(x)\lceil \log_{10}(x)\rceil of digits of a strictly positive integer xx.

Once can also import all functions from a library:

%%optlite -h 300
from math import *  # import all except names starting with an underscore

print("{:.2f}, {:.2f}, {:.2f}".format(sin(pi / 6), cos(pi / 3), tan(pi / 4)))
Loading...

The above uses the wildcard * to import (nearly) all the functions/variables provided in math.

%%optlite -h 500
print("{}".format(pow(-1, 2)))
print("{:.2f}".format(pow(-1, 1 / 2)))
from math import *

print("{}".format(pow(-1, 2)))
print("{:.2f}".format(pow(-1, 1 / 2)))
Loading...

To avoid name collisions, it is a good practice to use the full name (fully-qualified name) such as math.pow prefixed with the module.

%%optlite -h 350
import math

print("{:.2f}, {:.2f}".format(math.pow(-1, 2), pow(-1, 1 / 2)))
Loading...

Using the full name can be problematic if the name of a module is very long. There can even be a hierarchical structure. E.g., to plot a sequence using pyplot module from matplotlib package:

%matplotlib widget
import matplotlib.pyplot

matplotlib.pyplot.stem([4, 3, 2, 1])
matplotlib.pyplot.ylabel(r"$x_n$")
matplotlib.pyplot.xlabel(r"$n$")
matplotlib.pyplot.title("A sequence of numbers")
matplotlib.pyplot.show()
Loading...

In Python, modules can be structured into packages, which are themselves modules that can be imported. It is common to rename matplotlib.pyplot as plt:

import matplotlib.pyplot as plt

plt.stem([4, 3, 2, 1])
plt.ylabel(r"$x_n$")
plt.xlabel(r"$n$")
plt.title("A sequence of numbers")
plt.show()
Loading...

We can also rename a function as we import it to avoid name collision:

from math import pow as fpow

fpow(2, 2), pow(2, 2)
(4.0, 4)
%%optlite -h 500
import math as m

for m in range(5):
    m.pow(m, 2)
Loading...
Solution to Exercise 3

There is a name collision: m is assigned to an integer in the for loop and so it is no longer the module math when calling m.pow.

Documentation

Understanding how to properly document a function is crucial for maintaining clear and efficient code. It also allow others to use the code properly to avoid bugs. How should one go about documenting a function effectively? As an example:

# Author: John Doe
# Last modified: 2020-09-14
def increment(x):
    """Increment by 1.

    A simple demo of
    - parameter passing,
    - return statement, and
    - function documentation."""
    return x + 1  # + operation is used and may fail for 'str'

The help command shows the docstring we write

  • at the beginning of the function body
  • delimited using triple single/double quotes.
help(increment)
Help on function increment in module __main__:

increment(x)
    Increment by 1.

    A simple demo of
    - parameter passing,
    - return statement, and
    - function documentation.

The docstring should contain the usage guide, i.e., information for new users to call the function properly. See Python style guide (PEP 257) for

We can also annotate the function with type hints to indicate the types of the arguments and return value.

# Author: John Doe
# Last modified: 2020-09-14
def increment(x: float) -> float:
    """Increment by 1.

    A simple demo of
    - parameter passing,
    - return statement, and
    - function documentation."""
    return x + 1  # + operation is used and may fail for 'str'


help(increment)
Help on function increment in module __main__:

increment(x: float) -> float
    Increment by 1.

    A simple demo of
    - parameter passing,
    - return statement, and
    - function documentation.

Annotations, if done right, can make the code easier to understand. However, annotations are not enforced by the Python interpreter.[4]

def increment_user_input():
    return increment(input())  # does not raise error even though input returns str

Does calling the function lead to any error:

increment_user_input()

The types can also be described in the docstring following the Numpy or Google style.

# Author: John Doe
# Last modified: 2020-09-14
def increment(x: float) -> float:
    """Increment by 1.

    A simple demo of
    - parameter passing,
    - return statement, and
    - function documentation.

    Parameters
    ----------
    x: float
        Value to be incremented.

    Returns
    -------
    float:
        Value of x incremented by 1.
    """
    return x + 1  # + operation is used and may fail for 'str'


help(increment)
Help on function increment in module __main__:

increment(x: float) -> float
    Increment by 1.

    A simple demo of
    - parameter passing,
    - return statement, and
    - function documentation.

    Parameters
    ----------
    x: float
        Value to be incremented.

    Returns
    -------
    float:
        Value of x incremented by 1.

Can GenAI help us write documentations?

%%ai
Add the document string to the following function:
--
def increment(x: float) -> float:
    return x + 1  # + operation is used and may fail for 'str'
Loading...
Footnotes
  1. Note that the colon in lambda ...: cannot be followed by line break because it expects an expression rather than a suite.

  2. Use the keyboard interrupt (■) to stop the execution.

  3. An efficient implementation often uses the CORDIC algorithm.

  4. Type checking may be enforced by a supporting editor or packages such as pydantic and mypy.