8 Code Blocks: Importing Functions and Libraries
8.1Lesson goals¶
Import previously made functions into a program.
Utilize docstrings to document functions.
Find function and library documentation in Python and online.
8.2Overview¶
In a previous lesson, we covered how we can use functions to bundle up reusable code blocks. These functions will work in the interactive Python shell space or the Python file where we defined it, but what if we want to use it in another interactive notebook or Python file? Are we forced to copy and paste these functions over to the new location if we plan on reusing them in another program?
An important “Pythonic” style convention is to avoid duplication of code whenever possible,
and with the import built-in keyword we can import Python code from one program into another, whether that is a
separate Python file or a JupyterLab notebook. In this lesson, we will demonstrate this ability by first creating a
basic Python code file that contains a function and then import this function into a JupyterLab notebook. Then we will
import and explore other built-in libraries, including the external library NumPy. Finally, we
will cover how to read and write to external files with Python.
8.3The import keyword¶
The import built-in keyword is used to import code
from a module
(i.e., a file that contains Python code) into your code. To learn how this all works, we will first create a Python
file that contains a function definition (a.k.a., a module!), and then we will import the module into a interactive
notebook using import.
8.3.1Step 1: Create the module¶
Open your IDE and copy the following code:
def resistance(i, v):
# Use Ohm's law to calculate electrical resistance
r = v / i
# return resistance
return rSave the file as resistor.py to a convenient working folder.
8.3.2Step 2: Import the module using the import keyword¶
Now launch a new interactive Python file (usually a IPython notebook file → a IPYNB file) in the same folder
where you saved resistor.py. Execute the following command:
import resistor
r = resistor.resistance(i=10, v=5)
print(f"The resistance is {r} ohms")The resistance is 0.5 ohms
The import resistor statement tells the interpreter to scan for any files in your Python
env folder and your current directory that are
named resistor.py. If the interpreter finds the file it then imports the entire file as a module object named
resistor (i.e., it names the module object based on the filename).
The second command calls the resistance function using resistor.resistance() since the function is inside the
resistor module. Since this is not a built-in Python function so we to tell the interpreter where to find the
function! We save the output from the function call to the variable r and then print it out to the shell.
Since all data in Python is an object, our imported resistor.py file is also an object part of the module class.
You can see this by using type() on resistor:
print(type(resistor))<class 'module'>
To cut down on keystrokes, we can alternatively import just resistance() from the resistor module via
the command:
from resistor import resistance
r = resistance(i=10, v=5)
print(f"The resistance is {r} ohms")The resistance is 0.5 ohms
The from keyword can only be used with the
import keyword to import specific code from a Python module. The from resistor import resistance import pattern
means that the module resistor will be inspected to find some Python code that is named resistance, which will then
be imported. You can import any Python code from a module, like a variable, function, class, or in fact another module.
The from - import command structure cuts down on typing and typically lowers memory usage but sometimes
can make it harder to understand the origins of a function.
Let us create a new Python file in the same folder as the resistor.py file. Open up your IDE of choice, type the following code, and save it as ohms_law.py:
from resistor import resistance
def voltage(i, r):
# return voltage
return i * r
def current(v, r):
# return current
return v / rNow we can import multiple things from the ohms_law.py file as a module:
from ohms_law import resistance, voltage
r = resistance(v=10, i=0.25)
v = voltage(i=0.25, r=r)
i = current(v=10, r=r)
print(f"The voltage is {v} V")
print(f"The current is {i} A")
print(f"The resistance is {r} ohms")The voltage is 10.0 V
The current is 0.25 A
The resistance is 40.0 ohms
In this example, resistance() is imported via a two-step route: it is first called via the ohms_law module import
(ohms_law.py), which then imports resistance() from resistor.py. We also imported another function and variable
from the ohms_law module by typing their names using the statement import resistance, voltage. Notice
that we use a comma to separate the various objects we wished to import. We did not import current from ohms_law,
so that function is unavailable to our code. It is usually a good practice to import the specific functionality you
need from a module with from - import pattern instead of importing the top level module, but that is by no means a
hard rule.
8.3.3Example: Importing your work¶
Let’s expand out import skills even further by importing two different modules. First, create a Python file called
user.py and copy the function user(). Save this in the same folder as your ohms_law.py file.
Next, open up an interactive Python session in the same folder as your user.py and your ohms_law.py files and
create a code block that does the following:
Returns your name and age in months using the
user()function from earlier.Returns the resistance, voltage, and current using the
resistance(),voltage(), andcurrent()functions. Assume the voltage is and the current is . Import the needed functions using thefrom-importcommand structure.
Solution:
A copy of user.py can be found here. The code block below loads in these two modules and
uses the associated functions to solve the problem:
# Libraries
from ohms_law import resistance, voltage, current
from user import user
# Print user name
user_first_name, user_last_name, user_age = user(first="Goldy", last="Gopher",
age=40)
print("User's first name:", user_first_name)
print("User's last name:", user_last_name)
print("User's age in months:", user_age)
# Ohm's law analysis
r = resistance(v=10, i=0.25)
v = voltage(i=0.25, r=r)
i = current(v=10, r=r)
print(f"The voltage is {v} V")
print(f"The current is {i} A")
print(f"The resistance is {r} ohms")User's first name: Goldy
User's last name: Gopher
User's age in months: 480
The voltage is 10.0 V
The current is 0.25 A
The resistance is 40.0 ohms
The , character after each function in the from ohms_law import line allows us to list multiple objects to be
imported from a common module. We could have written the from - import scheme two times over with each function,
but the above example is more concise. As seen above, all functions work and now are deployable in our code!
8.4Docstrings and help()¶
It is important to document how your function operates in case another user (or even yourself) would like to reuse
your function on a later date. A
docstring is a
string that follows after the function definition statement (i.e., the line with def) and provides a place to
describe what a function does, the arguments that are passed into the function, details about the arguments, and the
function returns.
Docstrings are built into all functions in Python. To create a docstring we use the multi-line string notation
(i.e., a str that starts and ends with three single or double quotes) on the line after the def line. The code
below demonstrates how a docstring can be used with our convert_temp() function from earlier:
def convert_temp(temp, input_unit='F', output_unit='K'):
"""
Convert temperature from one unit to another. Returns temperature and
output_unit.
Arguments:
temp: Numeric temperature to be converted
input_unit: String designating the input unit. Can be one of 'F', 'C',
'R', 'K'. Default is 'F'
output_unit: String designating the output unit. Can be one of 'F', 'C',
'R', 'K'. Default is 'K'
Returns:
new_temp: New temperature
output_unit: Unit of the new temperature
"""
# Convert input temp to Kelvin
if input_unit == "F":
temp_k = (temp - 32) * (5 / 9) + 273.15
elif input_unit == "C":
temp_k = temp + 273.15
elif input_unit == "R":
temp_k = temp * (5 / 9)
# Convert Kelvin to desired output
if output_unit == "F":
new_temp = ((temp_k - 273.15) * (9 / 5)) + 32
elif output_unit == "C":
new_temp = temp_k - 273.15
elif output_unit == "R":
new_temp = temp_k * (9 / 5)
elif output_unit == "K":
new_temp = temp_k
return new_temp, output_unitAs seen above, docstrings utilize the multi-line string format in which the string content is bounded between
triple quotes (here we used """). As a side note, besides being used for docstrings, the multi-line string format
is a useful format whenever you need to create a string that needs to span multiple lines of code:
multi_line_string = """
We can type
multiple lines
in a triple quoted string.
"""
print(multi_line_string)
We can type
multiple lines
in a triple quoted string.
Docstrings act as the documentation for a function, so accessing this documentation is important when coding. One easy
way to access a function’s docstring is to utilize the built-in Python function
help(). The help() function is an extremely useful
tool when trying to understand how a function works. We will utilize help() many times going forward, but for now
let’s try using help() on our convert_temp() function by issuing the following command:
help(convert_temp)Help on function convert_temp in module __main__:
convert_temp(temp, input_unit='F', output_unit='K')
Convert temperature from one unit to another. Returns temperature and
output_unit.
Arguments:
temp: Numeric temperature to be converted
input_unit: String designating the input unit. Can be one of 'F', 'C',
'R', 'K'. Default is 'F'
output_unit: String designating the output unit. Can be one of 'F', 'C',
'R', 'K'. Default is 'K'
Returns:
new_temp: New temperature
output_unit: Unit of the new temperature
There are numerous ways to format a docstring. Sometimes you may see docstrings in this format:
def convert_temp(temp, input_unit='F', output_unit='K'):
"""
Convert temperature from one unit to another. Returns temperature and
output_unit.
:param temp: Temperature to converted.
:type: float
:param input_unit: Units of input temperature. Can be one of 'F',
'C', 'R', 'K'. Default is 'F'.
:type: str
:param output_unit: String designating the output unit. Can be one of 'F',
'C', 'R', 'K'. Default is 'K'.
:type: str
:return new_temp: Converted temperature.
:type: float
:return output_unit: Unit of converted temperature.
:type: str
"""
# Convert input temp to Kelvin
if input_unit == "F":
temp_k = (temp - 32) * (5 / 9) + 273.15
elif input_unit == "C":
temp_k = temp + 273.15
elif input_unit == "R":
temp_k = temp * (5 / 9)
# Convert Kelvin to desired output
if output_unit == "F":
new_temp = ((temp_k - 273.15) * (9 / 5)) + 32
elif output_unit == "C":
new_temp = temp_k - 273.15
elif output_unit == "R":
new_temp = temp_k * (9 / 5)
elif output_unit == "K":
new_temp = temp_k
return new_temp, output_unitThis is an example of the ReStructuredText
format for styling docstrings. By developing a docstring format, documentation generators can automatically
create documentation based on the docstring of the functions and classes. It is important to note the
ReStructuredText keywords :param, :type, and :return: are not part of the function definition, they are just
helpful hints for documentation generation.
Another ReStructuredText based-docstring style uses indents and underscores to define input arguments and returns.
Below is the convert_temp_docs() function from above but with this alternative formatting schema:
def convert_temp(temp, input_unit='F', output_unit='K'):
"""
Convert temperature from one unit to another. Returns temperature and
output_unit.
Parameters
----------
temp : float
Temperature to converted.
input_unit : str
Units of input temperature. Can be one of 'F', 'C', 'R', 'K'.
Default is 'F'.
output_unit : str
String designating the output unit. Can be one of 'F', 'C', 'R', 'K'.
Default is 'K'.
Returns
-------
new_temp : float
Converted temperature.
output_unit : str
Unit of converted temperature.
"""
# Convert input temp to Kelvin
if input_unit == "F":
temp_k = (temp - 32) * (5 / 9) + 273.15
elif input_unit == "C":
temp_k = temp + 273.15
elif input_unit == "R":
temp_k = temp * (5 / 9)
# Convert Kelvin to desired output
if output_unit == "F":
new_temp = ((temp_k - 273.15) * (9 / 5)) + 32
elif output_unit == "C":
new_temp = temp_k - 273.15
elif output_unit == "R":
new_temp = temp_k * (9 / 5)
elif output_unit == "K":
new_temp = temp_k
return new_temp, output_unitThere are several other style guides that exist for Python docstrings, and you can search for them online to find one that works for you. By following the rules of a docstring style guide will allow clear and readable documentation of your code.
8.4.1Example: Documenting your work¶
Using what you have learned so far with this guide, create a function that converts a force between one of the three most commonly used force units (i.e., newtons (N), pound-force (lbf), and dyne (dyn)). The conversion between the three unit scales is,
1 N = 0.225 lbf = 100,000 dyn
Input arguments should include the starting force’ value, the starting force’s units, and the desired units. Set default values for the initial units and the final units to be newtons and pound-force, respectively. Include a way for the function to display an error if the starting or desired units are not one of the three units. Finally, include a docstring that documents the function.
Solution:
There are a few ways to code this function. The example code below builds off of the
earlier temperature conversion function by first converting the starting force to newtons and
then converting to the desired force. A chain of if, elif, and else commands are used for the logic checks. Error
handling is done by passing strings through the function that state that an error has been caused. These can be
registered by viewing the returned variables. While this is works for our needs, there are more effective ways to
address error states using logging reports called tracebacks. An
upcoming lesson will show you how to utilize tracebacks for documenting errors. Finally,
note the use of the ReStructuredText documentation format for the docstring. While not mandatory when creating
function, adding a docstring, even if it is written in plain text, is useful for explaining how function operates.
def convert_force(initial_force, initial_units="N", converted_units="lbf"):
"""
Convert force between newtons, pound force, and dynes. Returns the
converted force and units.
Parameters
----------
initial_force : float
Force to be converted.
initial_units : str
Initial units of force. Allowable values are "N", "lbf", and "dyn".
Default value is "N"
converted_units : str
Units of force to be converted to. Allowable values are "N", "lbf",
and "dyn". Default value is "N"
Returns
-------
converted_force : float
Converted force value.
converted_units : str
Units of converted force. Default value is "lbf".
"""
# Convert force to N to standardize
if initial_units == "N":
force_N = initial_force
elif initial_units == "lbf":
force_N = initial_force / 0.225
elif initial_units == "dyn":
force_N = initial_force / 100000
else:
force_N = "\"incorrect initial force units\""
# Convert to new units
if force_N == "\"incorrect initial force units\"":
converted_force = force_N
elif converted_units == "N":
converted_force = force_N
elif converted_units == "lbf":
converted_force = force_N * 0.225
elif converted_units == "dyn":
converted_force = force_N * 100000
else:
converted_force = "\"incorrect converted force units\""
converted_units = ""
return converted_force, converted_unitsTo test the basic “functionality” of this function, the code below converts 10 N of force to pounds-force. We should get 2.25 lbf.
converted_force, converted_units = convert_force(10)
print(f"The converted force is {converted_force} {converted_units}.")The converted force is 2.25 lbf.
Since we are using the default values for initial_units and converted_units, we do not need to include them in
the argument list. For readability, however, it is sometimes better to include them:
converted_force, converted_units = convert_force(10,
initial_units="N",
converted_units="lbf")
print(f"The converted force is {converted_force} {converted_units}.")The converted force is 2.25 lbf.
Let’s now try converting 2,536.2 dyn to newtons. We should get 0.025362 N, which is shown in the code below:
converted_force, converted_units = convert_force(2536.2,
initial_units="dyn",
converted_units="N")
print(f"The converted force is {converted_force} {converted_units}.")The converted force is 0.025362 N.
The code block below demonstrates how the function handles an incorrect desired unit:
converted_force, converted_units = convert_force(2536.2,
initial_units="dyn",
converted_units="bad")
print(f"The converted force is {converted_force} {converted_units}.")The converted force is "incorrect converted force units" .
Finally, can issue the help() command to prove that the docstring works correctly:
help(convert_force)Help on function convert_force in module __main__:
convert_force(initial_force, initial_units='N', converted_units='lbf')
Convert force between newtons, pound force, and dynes. Returns the
converted force and units.
Parameters
----------
initial_force : float
Force to be converted.
initial_units : str
Initial units of force. Allowable values are "N", "lbf", and "dyn".
Default value is "N"
converted_units : str
Units of force to be converted to. Allowable values are "N", "lbf",
and "dyn". Default value is "N"
Returns
-------
converted_force : float
Converted force value.
converted_units : str
Units of converted force. Default value is "lbf".
Depending on your programming environment, your help() return may display the docstring in either plain text or as
ReStructuredText format. Here, the shell returns the docstring in a plain text format.
8.5Python libraries¶
So far we have demonstrated how to create modules to import functions we have already created into other code. We can also import additional modules into Python made by other people.
A Python library is a collection of modules that are gathered into one package. There are two types of Python libraries: “built-in” libraries which are bundled along with Python and “external” libraries which are available online to download. There are over 400,000 external libraries which can be installed with a package manager. They are stored in the lib folder of the Python environment. In this section, let us explore some popular libraries.
8.5.1The random library¶
The random library is useful for generating random numbers. It is
a built-in library found in Python. Let us try importing random and utilizing some functions from it:
from random import randint, random, gauss
print("Random integer 0 - 75:", randint(0, 75))
print("Random float 0 - 1:", round(random(), 3))
print("Random float from gauss distribution with mean = 10 and std = 5:",
round(gauss(10, 5), 3))Random integer 0 - 75: 26
Random float 0 - 1: 0.227
Random float from gauss distribution with mean = 10 and std = 5: 3.229
The code block above tries out a few functions from random. The first function,
randint(), returns a random integer from the
range of the passed min and max values. Next, the function
random() is used to return a random float between
0 - 1. The last function used is gauss(), which
returns a random float from a Gaussian (normal) distribution.
8.5.2The math library¶
The math library is a simple and useful built-in library
that contains multiple functions and mathematical constants. The code below shows an example of importing and
via the the variable pi and the function log(), respectively:
from math import pi, log
van_der_pauw_constant = pi / log(2)
print("vdP constant:", round(van_der_pauw_constant, 4))vdP constant: 4.5324
math also has trigonometric functions so it is useful in many scientific and engineering applications!
8.5.3Example: Random trigonometry¶
Using both the random and math libraries, create a simple code block that displays the cosine of five random
numbers between . Output values should be floating point numbers with three significant figures after the
decimal point. You will need to look at math library’s documentation to see how to implement a cosine function in
Python.
Solution:
There are a few ways one can do this depending on your overall Python knowledge. Since we have not covered
object-oriented programming or the NumPy library yet, we will use a for loop to take
the cosine of five random numbers between . An example code block is shown below:
from random import random
from math import cos, pi
for i in range(5):
value = random() * (2 * pi)
value = round(value, 3)
output = cos(value)
output = round(output, 3)
print(f"The cosine of {value:.3f} is {output:.3f}.")The cosine of 3.929 is -0.706.
The cosine of 5.742 is 0.857.
The cosine of 5.992 is 0.958.
The cosine of 2.018 is -0.432.
The cosine of 0.283 is 0.960.
The time built-in library is useful for tracking how long a program has
currently run for or sleeping a Python program to pause execution. The code below imports
time() and
sleep() from the time library and then runs a for loop
where the program delays for one second and then prints out a timestamp:
from time import time, sleep
# Get current time -> use for start time
start_time = time()
for i in range(4):
# Sleep 1 second
sleep(1)
# Get a timestamp
timestamp = time() - start_time
print("timestamp:", round(timestamp, 4), "s")timestamp: 1.0003 s
timestamp: 2.0014 s
timestamp: 3.0028 s
timestamp: 4.0038 s
8.5.4The datetime library¶
Similar to the time library, the built-in datetime
library has useful functionality to parse and work with dates. The example below creates a datetime object named
today and then formats the object into a string with the .strftime() datetime method.
from datetime import datetime
today = datetime.today()
print("Datetime object:", today)
today_string = today.strftime("%B %d %Y")
print("Formatted date string:", today_string)Datetime object: 2026-09-08 21:39:03.748040
Formatted date string: September 08 2026
8.5.5The numpy library¶
NumPy is not a built-in Python library, but can be
quickly installed to your virtual Python environment. It is an open source library that focuses
on scientific computing. NumPy is a commonly used library for many scientific applications, and we will explore this
library further in a later chapter. For now, let us rewrite our example code from the math library
example, but now using NumPy:
import numpy as np
van_der_pauw_constant = np.pi / np.log(2)
print("VDP constant:", round(van_der_pauw_constant, 4))VDP constant: 4.5324
Notice in this example we introduce the as keyword in conjunction with import. The import - as pattern
allows you to import something using an alias name. Here, we import the numpy library using the alias np, which
is commonly done with this library to cut down on keystrokes. Therefore, we do not have to type out numpy.pi to
utilize the value of , but instead we can simply type np.pi.
8.6Finding documentation¶
There are a few ways we can investigate what functions and constants a library offers. One way we can do this
is to first import the library into Python and then pass its name through the help() function. The code block below
demonstrates using the math library (note: we have turned off the output on this code block since the output is
rather long!):
import math
help(math)Furthermore, Python modules are simply Python code files, so we can look at the source file to understand how a
library works. For example, the random source file can be in the
Python GitHub repository. This file also exists on your
computer, and when you call from random import random, you are importing the random.py file as a module.
Alternatively, you can search online to understand a library (e.g., “python docs library name”). Online documentation
often will include “Getting Started” guides and tutorials to get you familiar with the library. Since many libraries
have documentation that is unwieldy to display with the help() command, reading a library’s documentation is
often best suited through a web browser.
8.7Final thoughts¶
Extending on what we learned about how to create functions, we now know how to import Python code
from one file to another as a Python module. By developing a code base of Python files / modules, we can import common
functionality throughout our programs and JupyterLab notebooks. This can cut down on development time and make a
more manageable centralized code base. Whatever code you do make, it is important to document its functionality. Python
has as a built in structure with docstrings that allows you to fully document your functions (and as you will see soon,
more objects) for later understanding.
We can also import many built-in and external libraries in Python. We looked
at the random,
math,
time,
and datetime built-in libraries,
and briefly introduced the external library NumPy.
There are a lot of Python libraries out there, and it can be difficult to figure out which library is the best to use
or how we can go about writing our own library. But we can always run the command import this to remind ourselves of
how to write beautiful Python code.
import thisThe Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!