Packages, Modules and Libraries#

Import external libraries and organize your code into functional chunks. For interactive reading and executing code blocks Binder and find b05-pypckg.ipynb, or install Python and JupyterLab locally.

Requirements

Make sure to understand Python functions.

Import Packages or Modules#

Importing a package or module in Python makes external functions and other elements (such as objects) of modules accessible in a script. The functions and other elements are stored within another Python file (.py) in some /site_packages/ folder (directory) of the interpreter environments. Thus, to use a non-standard package, it needs to be downloaded and installed first. Standard Python packages (e.g., os, math) are always accessible and other can be added with conda or pip (read more pip-installing).

The Difference between a Module and a Package

Modules are single or multiple files that can be imported independently (e.g. import a script named a_module.py with import a_module). Packages are a collection of modules in a directory with a defined hierarchy, which also enables the import of individual modules (e.g. from a_package.module import a_function). Packages are therefore also modules, but with a hierarchy definition (i.e., a __path__ attribute and an __init__.py file). Sounds fuzzy? Read this section down to the bottom and come back here to re-read this note.

The os package provides basic system-terminal-like commands, for example, to manage folder directories. So let’s import this essential package:

import os
print(os.getcwd()) # print current working directory
print(os.path.abspath('')) # print directory of script running

Overview of Import Options#

Here is an overview of options to import packages or modules (hierarchical parts of packages):

Command

Description

Usage of attributes

import package-name

Import an original module

package.item()

import package-name as nick-name

Import module and rename (alias) it in the script

nick-name.item()

from package-name import item

Import only a function, class or other items

item()

from package-name import *

Import all items

item()

Example#

import matplotlib.pyplot as plt # import pyplot from the matplotlib module and alias it with plt
import math as m

x = []
y = []

for e in range(1, 10):
    x.append(e)
    y.append(e**2)

plt.plot(x, y)

What is the best way to import a package or module?#

There is no global answer to this question. However, be aware that from package-name import * overwrites any existing variable or other items in the script. Thus, only use * when you are aware of all contents of a module or package.

pi = 9.112 # define a float called pi 
print("Pi is not %1.3f." % pi)

from math import pi # this overwrites the before defined variable pi
print("Pi is %1.3f." % pi)

Tip

Define default import packages for JupyterLab’s IPython kernel (read more in the Python installation section).

What items (attributes, classes, functions) are in a module?#

Sometimes we want to explore modules or check variable attributes. This is achieved with the dir() command:

import sys
print(sys.path)
print(dir(sys.path))

a_string = "zabaglione"
print(", ".join(dir(a_string)))

Create a new Module#

In object-oriented programming and code factorization, writing custom, new modules is an essential task. To write a new module, first, create a new script. Then, open the new script and add some parameters and functions.

# icecreamdialogue.py
flavors = ["vanilla", "chocolate", "bread"]
price_scoops = {1: "two euros", 2: "three euros", 3: "your health"}
welcome_msg = "Hi, I only have " + flavors[0] + ". How many scoops do you want?"

icecreamdialogue.py can now either be executed as a script (nothing will happen visibly) or imported as a module to access its variables (e.g., icecreamdialogue.flavors):

import icecreamdialogue as icd
print(icd.welcome_msg)
scoops_wanted = 2
print("That makes {0} please".format(icd.price_scoops[scoops_wanted]))

Make Script Stand-alone#

As an alternative, we can append the call to items in icecreamdialogue.py in the script and run it as a stand-alone script by adding an if __name__ == "__main__": block:

# icecreamdialogue_standalone.py
flavors = ["vanilla", "chocolate", "bread"]
price_scoops = {1: "two euros", 2: "three euros", 3: "your health"}
welcome_msg = "Hi, I only have " + flavors[0] + ". How many scoops do you want?"


if __name__ == "__main__":
    print(welcome_msg)
    scoops_wanted = 2
    print("That makes {0} please".format(price_scoops[scoops_wanted]))

Now we can run icecreamdialogue_standalone.py in a terminal (e.g., Linux Terminal, PyCharm’s Terminal tab at the bottom of the window, or in Atom using platformio-ide-terminal).

C:\temp\ python icecreamdialogue_standalone.py

Note

Depending on the definition of system variables used in the Terminal environment, Python must be called with a different variable name than python (e.g., python3 on some Linux platforms).

Standalone Scripts with Input Parameters#

To make the script more flexible, we can define, for instance, scoops_wanted as an input variable of a function.

# icecreamdialogue_standalone_withinput.py
flavors = ["vanilla", "chocolate", "bread"]
price_scoops = {1: "two euros", 2: "three euros", 3: "your health"}
welcome_msg = "Hi, I only have " + flavors[0] + ". How many scoops do you want?"

def dialogue(scoops_wanted): #formerly in the __main__ statement
    print(welcome_msg)
    print("That makes {0} please".format(price_scoops[scoops_wanted]))

if __name__ == "__main__":
    # import the terminal function emulator sys
    import sys
    if len(sys.argv) > 1: # make sure input is provided
        # if true: call the dialogue function with the input argument
        dialogue(int(sys.argv[1]))

Now, we can run icecreamdialogue_standalone_withinput.py in a terminal.

C:\temp\ python3 icecreamdialogue_standalone.py 2

Initialization of a Package (Hierarchically Organized Module)#

Good practice involves that one script does not exceed 50-100 lines of code (except inline docs and multiline variables). In consequence, a package will most likely consist of multiple scripts that are stored in one folder and one core script serves for the initiation of the scripts. This core script is called __init__.py and Python will always invoke this script name in a package folder. Example structure of a module called icecreamery:

  • icecreamery (folder name)

    • __init__.py - package initiation Python script

    • icecreamdialogue.py - dialogue producing Python script

    • icecream_maker.py - virtual ice cream producing Python script

To automatically invoke the two relevant scripts (sub-modules) of the icecreamery module, the __init__.py needs to include the following:

# __init__.py
print(f'Invoking __init__.py for {__name__}') # not absolutely needed ..
import icecreamery.icecreamdialogue, icecreamery.icecream_maker
# example usage of the icecreamery package
import icecreamery
print(icecreamery.icecreamdialogue.welcome_msg)

Do you remember the dir() function? It is intended to list all modules in a package, but it does not do so unless we defined an __all__ list in the __init__.py.

# __init__.py with __all__ list
__all__ = ['icecreamdialogue', 'icecream_maker']

ThThe full example of the icecreamery_all package is also available in an icecream repository.

# example usage of the icecreamery package
from icecreamery_all import *
print(icecreamdialogue.welcome_msg)

Package Creation Summary#

A hierarchically organized package contains an __init__.py file with an __all__ list to invoke relevant module scripts. The structure of a module can be more complex than the above example list (e.g., with sub-folders). When you write a package, consider using meaningful script and variable names, along with appropriate documentation.

Exercise with logging

Implement a custom logger in your module with logger = logging.getLogger(__name__) (replace __name__ with for example my-module-log).

Reload (Re-import) a Package or Module#

Since Python3, reloading a module requires importing the importlib module first. Reloading makes only sense if you are actively writing a new module. To reload any module type:

import importlib
importlib.reload(my-module)

Learning Success Check-up#

Take the learning success test for this Jupyter notebook.