Python Packages: Organize Modules Into Directories
Python packages are directories that organize multiple modules into a hierarchical structure, allowing you to scale projects from a few files to thousands. The key to making a directory a package is the __init__.py file, which signals to Python that the directory should be treated as a namespace. This article teaches you how to create packages, use dot notation for imports, and build nested subpackages for real-world application architecture.
Key Takeaways
- A package is a directory containing an
__init__.pyfile; without it, Python treats directories as regular folders - Dot notation (
from package.module import name) is the standard way to import from packages and subpackages - The
__init__.pyfile can be empty or contain initialization code that runs when the package is imported - Subpackages (packages inside packages) create a multi-level hierarchy for organizing large projects and preventing naming conflicts
What Is the Difference Between a Module and a Package?
A module is a single .py file containing reusable code. A package is a directory containing multiple modules plus an __init__.py file. Think of a module as a single document and a package as a folder organizing related documents.
Packages solve the problem of scaling: as a project grows from 5 modules to 50, putting them all in one directory becomes unmanageable. Packages let you group related modules logically. For example, an e-commerce application might have:
ecommerce/
├── __init__.py
├── users/
│ ├── __init__.py
│ ├── auth.py
│ └── profiles.py
├── products/
│ ├── __init__.py
│ └── catalog.py
└── payments/
├── __init__.py
└── transactions.py
Each subdirectory (users, products, payments) is a subpackage, and each .py file inside is a module. This structure is immediately understandable to any Python developer.
How Do You Create a Package and Use __init__.py?
To create a package, create a directory and place an __init__.py file inside it. The __init__.py file can be completely empty, but its presence tells Python that the directory is a package and not just a regular folder.
Creating Your First Package
Start with a simple store application:
my_store/
├── main.py
└── store/
├── __init__.py
└── cart.py
Create the directory structure:
mkdir my_store
cd my_store
mkdir store
touch store/__init__.py
Add code to store/cart.py:
# store/cart.py
from typing import List
def add_item(cart: List[str], item: str) -> List[str]:
"""Add an item to the shopping cart."""
cart.append(item)
print(f"'{item}' added to the cart.")
return cart
def remove_item(cart: List[str], item: str) -> List[str]:
"""Remove an item from the shopping cart."""
if item in cart:
cart.remove(item)
print(f"'{item}' removed from the cart.")
return cart
def display_cart(cart: List[str]):
"""Display the contents of the cart."""
if not cart:
print("Your cart is empty.")
else:
print("--- Your Cart ---")
for i, item in enumerate(cart, 1):
print(f"{i}. {item}")
print("-" * 17)
Now, in main.py, import from the package using dot notation:
# main.py
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)
The syntax from store import cart tells Python: "Look inside the store package for the cart module and import it."
Using __init__.py to Expose Functions
By default, __init__.py is empty. But you can add code to it to simplify how users import from your package. This is called creating a public API.
# store/__init__.py
from .cart import add_item, remove_item, display_cart
Now users can import directly from the package:
# main.py
from store import add_item, display_cart
my_cart = []
add_item(my_cart, "Keyboard")
display_cart(my_cart)
This pattern is used by large libraries like NumPy and Pandas to provide clean, intuitive imports. Note the dot notation .cart—the leading dot means "relative import from this package."
How Do You Organize Projects Using Subpackages?
Subpackages are packages inside packages. Each subpackage must have its own __init__.py file. This multi-level structure is ideal for large applications.
Creating Subpackages
Extend the store example with utilities:
my_store/
├── main.py
└── store/
├── __init__.py
├── cart.py
└── utils/
├── __init__.py
├── formatting.py
└── validation.py
Create the utilities subpackage:
mkdir store/utils
touch store/utils/__init__.py
Add the formatting.py module:
# store/utils/formatting.py
def format_price(price: float) -> str:
"""Convert a float price to a currency string."""
return f"${price:.2f}"
def format_quantity(qty: int) -> str:
"""Format quantity with proper plural handling."""
return f"{qty} item{'s' if qty != 1 else ''}"
Add the validation.py module:
# store/utils/validation.py
def is_valid_item(item: str) -> bool:
"""Check if an item name is valid."""
return len(item) > 0 and len(item) <= 100
def is_valid_price(price: float) -> bool:
"""Check if a price is valid."""
return price >= 0
Now import from subpackages in main.py:
# main.py
from store import cart
from store.utils import formatting, validation
my_cart = []
cart.add_item(my_cart, "Monitor")
cart.display_cart(my_cart)
if validation.is_valid_item("Monitor"):
price = 299.99
formatted_price = formatting.format_price(price)
print(f"Price: {formatted_price}")
The pattern from store.utils import formatting navigates the package hierarchy: store (package) → utils (subpackage) → formatting (module).
What Naming and Import Conventions Should You Follow?
Python's packaging conventions promote consistency across all projects:
Package and module names:
- Use lowercase letters and underscores (e.g.,
my_package,user_profiles) - Avoid hyphens in package names (they are not valid Python identifiers)
- Keep names short and descriptive
Import styles:
- Prefer
from package import moduleoverimport package.module - Use relative imports inside packages (
.prefix) to avoid circular dependencies - Avoid wildcard imports (
from module import *) in packages; always list what you import
Organizing subpackages:
- Group related functionality (e.g., all user-related code in a
userspackage) - Keep the hierarchy shallow (3–4 levels max) to avoid confusion
- Use meaningful names that reflect functionality, not file names
# Good: clear hierarchy
from app.users.auth import login
from app.products.catalog import search_products
from app.utils.formatting import format_price
# Avoid: unclear or circular relationships
from app import login # Unclear where login comes from
from app.a.b.c.d.e import something # Too deeply nested
from app.users.cart import add_item # cart is payments, not users
How Do You Handle Circular Imports and Package Dependencies?
Circular imports occur when two modules try to import from each other, creating a dependency cycle. This is a common problem when organizing large packages.
Example of a circular import problem:
# store/cart.py
from store.products import get_price # cart imports products
def add_item_with_price(cart, item):
price = get_price(item)
cart.append((item, price))
# store/products.py
from store.cart import display_cart # products imports cart — CYCLE!
def get_price(item):
return 9.99
Solutions:
- Import inside functions: Move the import to where it is needed
- Use a third module: Create a utilities module that both can import from
- Restructure packages: Rearrange modules to eliminate the cycle
# Solution: import inside the function
def add_item_with_price(cart, item):
from store.products import get_price # Import here, not at module level
price = get_price(item)
cart.append((item, price))
Frequently Asked Questions
Can __init__.py be completely empty?
Yes. An empty __init__.py is sufficient to mark a directory as a package. Python 3.3+ supports namespace packages without __init__.py, but using __init__.py is still the standard in most projects because it gives you control over what gets imported.
What is the difference between from store import cart and from store.cart import add_item?
The first imports the cart module object; you access functions as cart.add_item(). The second imports the function directly; you call add_item() without the module prefix. The second is more concise if you only need a few items.
How do you import everything from a module using the wildcard?
Use from module import *. However, this is discouraged in production code because it is unclear what names are being imported and can cause naming conflicts. In __init__.py, use __all__ to control what gets exported:
# store/utils/__init__.py
__all__ = ["formatting", "validation"]
Can a package and a module have the same name?
No. If you have a package named utils and a module named utils.py in the same directory, Python gets confused. Avoid this situation by keeping naming conventions clear.
How do you run a package as a script?
Create a __main__.py file inside the package:
# store/__main__.py
if __name__ == "__main__":
print("Running the store package...")
Then run it as python -m store.