Skip to content

Module in Python

diagram a module's body runs once per process mermaid
import is not 'run this file'. Python checks sys.modules first, and only executes the file if the name is not already there -- so the second import of the same module is a dictionary lookup. That is why module-level code is effectively a one-time setup, and why editing a file has no effect on an already-running interpreter.

Unraveling the Mysteries of Python Modules: A Comprehensive Guide

Section titled “Unraveling the Mysteries of Python Modules: A Comprehensive Guide”

Python’s modular design is a cornerstone of its flexibility and scalability. Modules allow developers to organize code, promote reusability, and create well-structured applications. In this comprehensive guide, we’ll delve into the world of Python modules, exploring what they are, how to create them, and best practices for their effective use.

A Python module is a file containing Python definitions and statements. These files have a .py extension and can be considered as containers for Python code. Modules serve several purposes:

  1. Code Organization: Modules provide a way to organize Python code into separate files, making it easier to manage and maintain.

  2. Code Reusability: By encapsulating related functionality within modules, developers can reuse code across different parts of an application or even in different projects.

  3. Namespacing: Modules act as namespaces, preventing naming conflicts by encapsulating variables and functions within their scope.

Let’s start by creating a simple module. Consider a module named math_operations.py that contains basic mathematical operations:

math_operations.py
def add(x, y):
    return x + y
 
def subtract(x, y):
    return x - y
 
def multiply(x, y):
    return x * y
 
def divide(x, y):
    if y != 0:
        return x / y
    else:
        return "Cannot divide by zero"

In this example, the math_operations.py file defines four functions: add, subtract, multiply, and divide. Each function encapsulates a specific mathematical operation.

Once a module is created, it can be imported into other Python scripts or modules using the import statement. Consider a script named calculator.py that imports and uses the math_operations module:

calculator.py
import math_operations
 
result_add = math_operations.add(5, 3)
result_subtract = math_operations.subtract(5, 3)
result_multiply = math_operations.multiply(5, 3)
result_divide = math_operations.divide(5, 3)
 
print("Addition:", result_add)
print("Subtraction:", result_subtract)
print("Multiplication:", result_multiply)
print("Division:", result_divide)

Output:

command
C:\Users\Your Name> python calculator.py
Addition: 8
Subtraction: 2
Multiplication: 15
Division: 1.6666666666666667

In this script, the math_operations module is imported, and its functions are used to perform basic mathematical operations. The import statement makes all functions within the module accessible to the script.

In addition to the standard import syntax, Python offers alternative ways to import modules. For example, you can use the from ... import ... syntax to import specific functions or variables from a module:

calculator.py
from math_operations import add, multiply
 
result_add = add(5, 3)
result_multiply = multiply(5, 3)
 
print("Addition:", result_add)
print("Multiplication:", result_multiply)

Output:

command
C:\Users\Your Name> python calculator.py
Addition: 8
Multiplication: 15

This syntax directly imports the add and multiply functions from the math_operations module, allowing you to use them without referencing the module itself.

You can also use the from ... import * syntax to import all functions and variables from a module:

calculator.py
from math_operations import *
 
result_add = add(5, 3)
result_multiply = multiply(5, 3)
 
print("Addition:", result_add)
print("Multiplication:", result_multiply)

Output:

command
C:\Users\Your Name> python calculator.py
Addition: 8
Multiplication: 15

This syntax imports all functions and variables from the math_operations module, allowing you to use them without referencing the module itself.

Python allows you to assign aliases to module names, providing a way to reference modules with shorter names in your code. This can be particularly useful for modules with long names or when avoiding naming conflicts.

calculator.py
import math_operations as math_ops
 
result_add = math_ops.add(5, 3)
result_multiply = math_ops.multiply(5, 3)
 
print("Addition:", result_add)
print("Multiplication:", result_multiply)

Output:

command
C:\Users\Your Name> python calculator.py
Addition: 8
Multiplication: 15

In this example, the math_operations module is imported with the alias math_ops, allowing for a more concise reference.

To import modules from a package, you can use the dot notation. For example, importing the rectangle_area function from the shapes module within the geometry package:

calculator.py
from geometry.shapes import rectangle_area
 
result_area = rectangle_area(4, 6)
print("Rectangle Area:", result_area)

Output:

command
C:\Users\Your Name> python calculator.py
Rectangle Area: 24

Here, the rectangle_area function from the shapes module within the geometry package is imported and used in the script.

When you import a module, Python searches for it in specific directories. The module search path is a list of directories that Python checks in a specific order. Understanding how Python locates modules is crucial for managing module dependencies.

The module search path includes:

  1. The Current Directory: Python first searches for modules in the directory where the main script is located.

  2. PYTHONPATH Environment Variable: If the module is not found in the current directory, Python checks the directories specified in the PYTHONPATH environment variable.

  3. Standard Library Directories: Python includes a standard library containing built-in modules. These modules are readily available without additional installations.

  4. Site Packages: Python searches the site-packages directory, which contains third-party modules installed using tools like pip.

While modules provide a means of organizing code within a single file, packages offer a way to organize multiple modules into a hierarchical directory structure. A package is a directory containing a special __init__.py file and one or more module files. The __init__.py file can be empty, but it is necessary to mark the directory as a package.

Consider a package named geometry with the following structure:

command
geometry/

├── __init__.py
├── shapes.py
└── utils.py

The geometry package contains two modules: shapes.py and utils.py. The __init__.py file signifies that the directory is a package.

__init__.py
# __init__.py

In this example, the __init__.py file is empty. However, it can contain initialization code that is executed when the package is imported.

shapes.py
# shapes.py
def rectangle_area(length, width):
    return length * width
 
def square_area(side):
    return side * side
 
def circle_area(radius):
    return 3.14 * radius * radius

The shapes.py module defines three functions: rectangle_area, square_area, and circle_area. Each function encapsulates a specific geometric area calculation.

utils.py
# utils.py
def rectangle_perimeter(length, width):
    return 2 * (length + width)
 
def square_perimeter(side):
    return 4 * side
 
def circle_circumference(radius):
    return 2 * 3.14 * radius

The utils.py module defines three functions: rectangle_perimeter, square_perimeter, and circle_circumference. Each function encapsulates a specific geometric perimeter calculation.

Using packages, you can organize related modules into a hierarchical directory structure. This promotes a modular design that facilitates collaborative development.

To import modules from a package, you can use the dot notation. For example, importing the rectangle_area function from the shapes module within the geometry package:

calculator.py
# Importing from a package
from geometry.shapes import rectangle_area
 
result_area = rectangle_area(4, 6)
print("Rectangle Area:", result_area)

Output:

command
C:\Users\Your Name> python calculator.py
Rectangle Area: 24

Here, the rectangle_area function from the shapes module within the geometry package is imported and used in the script.

Python allows you to assign aliases to module names, providing a way to reference modules with shorter names in your code. This can be particularly useful for modules with long names or when avoiding naming conflicts.

calculator.py
# Module aliasing
import math_operations as math_ops
 
result_add = math_ops.add(5, 3)
result_multiply = math_ops.multiply(5, 3)
 
print("Addition:", result_add)
print("Multiplication:", result_multiply)

Output:

command
C:\Users\Your Name> python calculator.py
Addition: 8
Multiplication: 15

In this example, the math_operations module is imported with the alias math_ops, allowing for a more concise reference.

A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition. Docstrings are used to document code and provide information about the module, function, class, or method. They are accessible using the dot notation.

math_operations.py
# math_operations.py
"""This module contains basic mathematical operations."""
 
def add(x, y):
    """Adds two numbers."""
    return x + y
 
def subtract(x, y):
    """Subtracts two numbers."""
    return x - y
 
def multiply(x, y):
    """Multiplies two numbers."""
    return x * y
 
def divide(x, y):
    """Divides two numbers."""
    if y != 0:
        return x / y
    else:
        return "Cannot divide by zero"

In this example, the math_operations.py module contains a docstring that describes the module’s purpose. Each function also contains a docstring that describes its purpose.

calculator.py
# calculator.py
import math_operations
print(math_operations.__doc__)
print(math_operations.add.__doc__)

Output:

command
C:\Users\Your Name> python calculator.py
This module contains basic mathematical operations.
Adds two numbers.

Here, the module docstring and the add function docstring are printed. Docstrings are accessible using the dot notation.

Modules have several attributes that can be used to access information about the module. These attributes are accessible using the dot notation.

  • __name__ - Returns the name of the module
  • __file__ - Returns the path to the module file
  • __doc__ - Returns the module docstring
  • __package__ - Returns the name of the package containing the module
  • __dict__ - Returns a dictionary containing the module’s global variables
calculator.py
# calculator.py
import math_operations
print("Module Name:", math_operations.__name__)
print("Module File:", math_operations.__file__)
print("Module Docstring:", math_operations.__doc__)
print("Module Package:", math_operations.__package__)
print("Module Dictionary:", math_operations.__dict__)

Output:

command
C:\Users\Your Name> python calculator.py
Module Name: math_operations
Module File: /Users/username/Desktop/math_operations.py
Module Docstring: This module contains basic mathematical operations.
Module Package: None
Module Dictionary: {'__name__': 'math_operations', '__doc__': 'This module contains basic mathematical operations.', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7f9f3c2b5d90>, '__spec__': ModuleSpec(name='math_operations', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7f9f3c2b5d90>, origin='/Users/username/Desktop/math_operations.py'), '__file__': '/Users/username/Desktop/math_operations.py', '__cached__': None, 'add': <function add at 0x7f9f3c2b5e50>, 'subtract': <function subtract at 0x7f9f3c2b5ee0>, 'multiply': <function multiply at 0x7f9f3c2b5f70>, 'divide': <function divide at 0x7f9f3c2b5040>}

Here, the module attributes are printed. These attributes are accessible using the dot notation.

reload() - Reloads a previously imported module

Section titled “reload() - Reloads a previously imported module”

Python’s reload() function allows you to reload a previously imported module. This can be useful when testing code changes without restarting the interpreter. The reload() function is available in the importlib module.

calculator.py
def add(x, y):
    return x + y

Output:

command
C:\Users\username\Desktop>python
>>> import calculator
>>> calculator.add(5, 3)
8

Here, the add function is imported from the calculator module and used to perform a basic addition operation.

Now, let’s make a change to the add function and reload the module:

calculator.py
def add(x, y):
    return x + y + 1

Output:

command
C:\Users\username\Desktop>python
>>> import importlib
>>> importlib.reload(calculator)
<module 'calculator' from 'C:\\Users\\username\\Desktop\\calculator.py'>
>>> calculator.add(5, 3)
9

Here, the add function is reloaded from the calculator module, and the change is reflected in the output. The reload() function is available in the importlib module.

To maximize the benefits of using modules in Python, consider the following best practices:

Organize related functionality into separate modules. This promotes a clean and maintainable codebase.

Choose descriptive names for your modules to enhance code readability. Avoid generic names that may lead to naming conflicts.

Keep modules focused and avoid creating overly large modules. Smaller modules are easier to understand and maintain.

Include docstrings and comments within your modules to provide documentation for other developers (or yourself) who may use your code.

Be cautious of circular imports, where two or more modules depend on each other. This can lead to runtime errors.

Adhere to Python naming conventions, such as using lowercase letters with underscores for module and function names.

Consider using virtual environments to isolate project dependencies and prevent conflicts between different projects.

Be aware of how Python searches for modules to manage dependencies effectively.

When creating packages, choose meaningful names and structure them logically. Follow the Python package naming conventions.

Python modules are a fundamental building block for creating well-organized and scalable code. Whether you’re working with simple scripts or complex applications, understanding how to create, import, and manage modules is crucial. By leveraging modules and packages, you can enhance code organization, encourage code reuse, and foster a modular design that facilitates collaborative development. As you continue your journey in Python development, embracing modular programming practices will contribute to writing clean, maintainable, and efficient code. Happy coding!


sketch import runs the file once; every later import is a lookup p5.js
A module whose body prints was imported three ways in one process. Only the first execution printed. The others found the name already in sys.modules and bound the existing object without running anything, which is why module-level code is one-time setup and why editing a file has no effect on a process that is already running.
pch.quizTag pch.quizDefaultTitle
  1. A module with a `print()` at the top level is imported twice in one process. How many times does it print?

    pch.quizShowAnswer

    B — Once — Verified: the second `import` and `importlib.import_module` printed nothing. Both found the module already in `sys.modules`.

  2. How do you force a module's body to run again?

    pch.quizShowAnswer

    B — `importlib.reload(once)` — `reload` re-executes the body in the existing module object. Note that names other modules already imported FROM it still point at the old objects.

  3. Why do circular imports fail confusingly rather than cleanly?

    pch.quizShowAnswer

    B — The module is added to `sys.modules` BEFORE its body runs, so the second import finds a half-built module — That is exactly why you get `ImportError: cannot import name X` for a name defined later in the file, rather than a straightforward circular-import error.

  4. What is the practical consequence of module-level code running at first import?

    pch.quizShowAnswer

    B — It is startup cost for every entry point that imports the module, including tests — Expensive work at module level — reading a file, opening a connection, building a large table — is paid by anything that touches the module, whether or not it needs that work.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading