Skip to main content

Introduction to Packages: Organizing Modules into Directories

We've successfully organized our code into reusable modules. But what happens when your project grows even larger and you have dozens of modules? Throwing them all into the same directory becomes just as messy as having one giant file. The next level of organization in Python is the package. A package is simply a way to structure your project's modules into a directory hierarchy.


📚 Prerequisites

You should be comfortable with the concepts of creating and importing your own modules:

  • A module is a .py file.
  • You can use import to access code from another module.

🎯 Article Outline: What You'll Master

In this article, you will learn:

  • What a Package Is: Understand that a package is a directory of modules.
  • The Role of __init__.py: Learn about the special file that makes a directory a package.
  • Creating a Package: A step-by-step guide to creating your first package structure.
  • Importing from Packages: How to use dot notation to import modules from within your package structure.
  • Subpackages: How to create nested packages for even better organization.

🧠 Section 1: From Modules to Packages

While a module is a single .py file, a package is a collection of modules in a directory. Think of it like this: if a module is a single document, a package is a folder that organizes those documents.

This structure allows you to group related modules. For example, in a large e-commerce application, you might have a package for users, another for products, and a third for payments.


💻 Section 2: Creating Your First Package

To tell Python that a directory should be treated as a package, it must contain a special file named __init__.py. This file can be completely empty, but its presence is what signals to Python that this is a package.

Let's refactor our shopping_cart project from the last article into a proper package structure.

Our New Project Structure:

/my_store/
├── main.py
└── store/ <-- This is our package
├── __init__.py <-- Makes 'store' a package
└── cart.py <-- Our module, renamed for clarity

Step 1: Create the Directory and Files

  1. Create a main project folder called my_store.
  2. Inside my_store, create the main.py file.
  3. Inside my_store, create a directory called store.
  4. Inside store, create an empty file named __init__.py.
  5. Move the code from our old shopping_cart.py into a new file inside store called cart.py.

Your store/cart.py file should look like this:

# store/cart.py

"""A module for managing a shopping cart."""
from typing import List

def add_item(cart: List[str], item: str) -> List[str]:
# ... (function logic)
pass # Keep the same function logic as before

def remove_item(cart: List[str], item: str) -> List[str]:
# ... (function logic)
pass # Keep the same function logic as before

def display_cart(cart: List[str]):
# ... (function logic)
pass # Keep the same function logic as before

Step 2: Importing from the Package

Now, we need to update main.py to import from our new store package. We do this using dot notation.

The syntax is from package_name import module_name.

# main.py

# Import the 'cart' module from the 'store' package
from store import cart

my_cart = []

cart.display_cart(my_cart)

my_cart = cart.add_item(my_cart, "Laptop")
my_cart = cart.add_item(my_cart, "Mouse")

cart.display_cart(my_cart)

This is much more organized! Now, if we wanted to add logic for managing user profiles, we could add a profiles.py module inside the store package without cluttering our main directory.


🛠️ Section 3: Subpackages for Deeper Organization

You can nest packages inside other packages. These are called subpackages. Each subdirectory must contain its own __init__.py file to be considered a subpackage.

Let's add a utils (utilities) subpackage to our store for helper functions.

Our Final Project Structure:

/my_store/
├── main.py
└── store/
├── __init__.py
├── cart.py
└── utils/ <-- Our subpackage
├── __init__.py <-- Makes 'utils' a subpackage
└── formatting.py <-- A new module in the subpackage

Step 1: Create the formatting.py Module

# store/utils/formatting.py

def format_price(price: float) -> str:
"""Formats a float into a currency string."""
return f"${price:.2f}"

Step 2: Use the Subpackage in main.py

We use the same dot notation to go deeper into the structure.

# main.py

from store import cart
from store.utils import formatting # Import from the subpackage

my_cart = []
my_cart = cart.add_item(my_cart, "Keyboard")
cart.display_cart(my_cart)

# Let's imagine the keyboard costs 75.0
price_str = formatting.format_price(75.0)
print(f"The formatted price is: {price_str}")

# Output:
# 'Keyboard' added to the cart.
# --- Your Cart ---
# 1. Keyboard
# -----------------
# The formatted price is: $75.00

🚀 Section 4: The Purpose of __init__.py

While __init__.py can be empty, it can also contain Python code. This code is executed when the package (or a module within it) is imported.

A common use for __init__.py is to make functions or classes from modules inside the package directly accessible at the package level, creating a simpler API for your package.

For example, in store/__init__.py, you could write:

# store/__init__.py

# Make the add_item function available directly from the 'store' package
from .cart import add_item

Now, in main.py, you could simplify your import:

# main.py
from store import add_item # No need to import from store.cart

my_cart = []
add_item(my_cart, "Webcam") # Call it directly

This is an advanced technique but is good to be aware of as you start exploring other libraries.


✨ Conclusion & Key Takeaways

Packages are the standard way to structure Python applications and libraries. By organizing your modules into a logical directory hierarchy, you create a project that is scalable, maintainable, and easy for other developers to understand.

Let's summarize the key takeaways:

  • A Package is a Directory: Specifically, a directory containing an __init__.py file.
  • __init__.py is the Key: This (often empty) file tells Python to treat a directory as a package.
  • Use Dot Notation to Import: Access modules within packages using from package.subpackage import module.
  • Subpackages Provide Structure: You can nest packages within each other to create a clear and organized project hierarchy.

Challenge Yourself: Create a package named reports. Inside reports, create two modules: sales.py and inventory.py. Each module should have a simple function (e.g., generate_sales_report() and generate_inventory_report()). Then, create a main.py outside the package that imports and calls both functions.


➡️ Next Steps

You now know how to organize your own code. But what about using code written by the vast Python community? In our next article, we'll dive into the Python Standard Library and explore some of its most useful modules.

Happy structuring!