Python Modules and Packages

python-module-package

If you’re new to Python, you might wonder how to organize your code as your projects grow. Python’s modules and packages are the answer! They help you structure your code into reusable, manageable pieces. In this guide, we’ll explain modules and packages in a way that’s easy to understand, with plenty of examples. We’ll cover how to use built-in modules, create your own, and organize them into packages. Let’s get started!

Python Modules

A module in Python is simply a .py file that contains code, such as functions, classes, or variables. Think of it as a toolbox: you can store useful tools (code) in a module and use them in other programs whenever you need them.

Part 1a: How to Use Modules

Importing Built-in Modules

Python comes with many built-in modules that provide useful functionality. For example, the math module has mathematical functions, and the datetime module helps you work with dates and times.

Here’s how to import and use a built-in module:

import math

# Use functions from the math module
print(math.sqrt(16))  # Output: 4.0
print(math.pi)        # Output: 3.141592653589793

You can also import specific parts of a module using from:

from datetime import datetime

# Get the current date and time
current_time = datetime.now()
print(current_time)  # Output: 2025-09-18 06:56:23.123456 (example timestamp)

Creating Your Own Module

You can create your own module by writing a .py file with your code. Let’s create a module called math_utils.py with some useful functions.

math_utils.py:

def add(a, b):
    """Returns the sum of two numbers."""
    return a + b

def multiply(a, b):
    """Returns the product of two numbers."""
    return a * b

PI = 3.14159  # A constant

Now, you can import and use this module in another file, say main.py, in the same directory:

main.py:

import math_utils

# Use functions and variables from math_utils
result1 = math_utils.add(5, 3)
result2 = math_utils.multiply(4, 2)
print(f"Sum: {result1}")         # Output: Sum: 8
print(f"Product: {result2}")     # Output: Product: 8
print(f"PI: {math_utils.PI}")    # Output: PI: 3.14159

Using Aliases

Sometimes, module names are long or you want a shorter name for convenience. You can use the as keyword to create an alias:

import math_utils as mu

print(mu.add(10, 20))  # Output: 30

This is common with popular libraries, like:

import numpy as np

array = np.array([1, 2, 3])
print(array)  # Output: [1 2 3]

Part 1b: Key Concepts for Modules

The __name__ == “__main__” Check

Every Python module has a special variable called __name__. When you run a .py file directly, __name__ is set to “__main__”. When the file is imported as a module, __name__ is set to the module’s name.

This lets you write code that runs only when the module is executed directly, not when it’s imported. Here’s an example:

math_utils.py (updated):

def add(a, b):
    """Returns the sum of two numbers."""
    return a + b

def multiply(a, b):
    """Returns the product of two numbers."""
    return a * b

PI = 3.14159

if __name__ == "__main__":
    # This code only runs if the file is executed directly
    print("Testing math_utils module...")
    print(f"Add: {add(2, 3)}")      # Output: Add: 5
    print(f"Multiply: {multiply(4, 5)}")  # Output: Multiply: 20

main.py:

import math_utils

print(math_utils.add(7, 8))  # Output: 15

When you run main.py, the if __name__ == “__main__”: block in math_utils.py won’t execute because math_utils is being imported, not run directly. But if you run math_utils.py directly (e.g., python math_utils.py), the test code will run.

Using dir(module)

The dir() function shows you all the attributes (functions, variables, etc.) available in a module. This is great for exploring what a module contains.

Example:

import math_utils

print(dir(math_utils))
# Output: ['PI', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add', 'multiply']

This lists everything in math_utils, including our functions (add, multiply) and the constant PI.

Organizing Code into Multiple Files

As your project grows, you’ll want to split your code into multiple files (modules) to keep it organized. For example:

  • One module for math-related functions (math_utils.py).

  • Another for string-related functions (string_utils.py).

  • A main script (main.py) to use these modules.

This makes your code easier to read, maintain, and reuse.

Example: A Real-World Module

Let’s create a practical example. We’ll update math_utils.py with more functions and use it in main.py.

math_utils.py:

def add(a, b):
    """Returns the sum of two numbers."""
    return a + b

def multiply(a, b):
    """Returns the product of two numbers."""
    return a * b

def square(num):
    """Returns the square of a number."""
    return num * num

PI = 3.14159

if __name__ == "__main__":
    print("Testing math_utils...")
    print(f"Add: {add(2, 3)}")
    print(f"Square: {square(4)}")

main.py:

import math_utils as mu

# Use the module's functions and constant
print(f"Sum: {mu.add(10, 5)}")        # Output: Sum: 15
print(f"Square: {mu.square(3)}")       # Output: Square: 9
print(f"Area of circle: {mu.PI * mu.square(2)}")  # Output: Area of circle: 12.56636

This example shows how to create a reusable module and import it into your main program.

Python Packages

A package is a way to group multiple modules together. It’s a directory containing a special file called __init__.py and one or more .py files (modules). Packages help you organize larger projects.

Part 2a: Understanding Packages

Package Structure

Here’s what a package looks like:

mypackage/
    __init__.py
    module1.py
    module2.py
  • The __init__.py file tells Python that this directory is a package. It can be empty or contain initialization code.

  • module1.py and module2.py are modules with your code.

Let’s create a package called mypackage with two modules.

mypackage/module1.py:

def greet(name):
    """Greets a user by name."""
    return f"Hello, {name}!"

mypackage/module2.py:

def farewell(name):
    """Says goodbye to a user."""
    return f"Goodbye, {name}!"

mypackage/init.py:

# This file can be empty or include imports to make module contents accessible

Now, you can use this package in a main script.

main.py (in the parent directory of mypackage):

from mypackage import module1
from mypackage.module2 import farewell

print(module1.greet("Alice"))    # Output: Hello, Alice!
print(farewell("Bob"))          # Output: Goodbye, Bob!

Relative vs. Absolute Imports

You can import modules within a package using absolute or relative imports.

  • Absolute imports use the full path from the package name:

from mypackage.module1 import greet
  • Relative imports use a dot (.) to refer to the current or parent directory. For example, inside module2.py, you could import module1:

mypackage/module2.py (updated):

from .module1 import greet  # Relative import

def farewell(name):
    """Says goodbye after a greeting."""
    return f"{greet(name)} Goodbye, {name}!"

Now, in main.py:

from mypackage.module2 import farewell

print(farewell("Alice"))  # Output: Hello, Alice! Goodbye, Alice!

The . in from .module1 means “look in the same directory as this module.”

Part 2b: Best Practices for Packages

Splitting Code into Modules and Packages

To keep your code maintainable:

  • Group related functions into modules (e.g., math_utils.py for math functions, string_utils.py for string functions).

  • Group related modules into packages (e.g., a utils package with math_utils.py and string_utils.py).

  • Use clear, descriptive names for modules and packages.

Using __all__ in __init__.py

The __all__ list in __init__.py controls what gets imported when someone uses from mypackage import *. This is a good way to avoid cluttering the namespace.

mypackage/init.py (updated):

__all__ = ["module1"]  # Only module1 will be imported with *

from . import module1

main.py:

from mypackage import *

print(module1.greet("Alice"))  # Output: Hello, Alice!
# module2.farewell would raise an error because it's not in __all__

By specifying __all__, you control which modules are exposed, making your package cleaner and safer to use.

Example: A Practical Package

Let’s create a package called calculations with two modules: basic.py and advanced.py.

calculations/basic.py:

def add(a, b):
    """Returns the sum of two numbers."""
    return a + b

def subtract(a, b):
    """Returns the difference of two numbers."""
    return a - b

calculations/advanced.py:

from .basic import add  # Relative import

def square(num):
    """Returns the square of a number."""
    return add(num, num) / 2  # Uses add from basic.py

calculations/init.py:

__all__ = ["basic", "advanced"]

from . import basic
from . import advanced

main.py:

from calculations.basic import add, subtract
from calculations.advanced import square

print(add(5, 3))       # Output: 8
print(subtract(5, 3))  # Output: 2
print(square(4))       # Output: 8.0 (because 4 + 4 = 8)

This example shows a well-organized package with relative imports and __all__ to control exports.

Conclusion

Python modules and packages are essential for organizing your code. Modules let you store reusable code in .py files, while packages group related modules into a directory with an __init__.py file. By using built-in modules like math and datetime, creating your own modules, and organizing them into packages, you can write clean, maintainable code.

Key takeaways:

  • Use import to access built-in or custom modules.

  • Use __name__ == “__main__” to run test code only when a module is executed directly.

  • Use dir() to explore a module’s contents.

  • Organize large projects with packages, using __all__ to control imports.

  • Practice relative and absolute imports to understand how modules interact within a package.

Try creating your own modules and packages, like the math_utils or calculations examples, to get comfortable with these concepts. With practice, you’ll be organizing Python projects like a pro!

Leave a Reply

Your email address will not be published. Required fields are marked *