As the scale of a program grows and the number of module files (.py) increases, you will likely want to organize them into a single directory (folder). In Python, a collection of multiple modules organized by directory is called a “Package.” Packaging allows you to separate namespaces by functionality and keep large applications organized.
This article explains the steps to create a custom package and the role of the __init__.py file in controlling package behavior.
Basic Structure of a Package
To create a package in Python, you basically follow these two steps:
- Create a folder (the folder name becomes the package name).
- Place module files (
.py) and a special file named__init__.pyinside that folder.
Example Directory Structure
Here is a configuration for creating a package named math_tools that provides calculation functions.
project_root/
│
├─ main.py <-- Script to execute
│
└─ math_tools/ <-- This is the package
│
├─ __init__.py <-- Package initialization file
├─ basic.py <-- Module 1
└─ geometry.py <-- Module 2
Note on __init__.py: In Python 3.3 and later, a directory is recognized as a package even without __init__.py (Namespace Package). However, it is still common practice to create it to explicitly treat it as a regular package and to perform initialization processing. The content can be empty.
1. Creating Modules
First, create the modules that will be the contents of the package.
math_tools/basic.py
def add(a, b):
"""Simple addition"""
return a + b
def subtract(a, b):
"""Simple subtraction"""
return a - b
math_tools/geometry.py
class Rectangle:
"""Rectangle Class"""
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
2. Using the Package (Standard Import)
To use the created package, import it from main.py as follows. Use a dot (.) to traverse the hierarchy like package_name.module_name.
main.py
# Method 1: Specify modules to import
from math_tools import basic, geometry
# Usage
result = basic.add(10, 5)
rect = geometry.Rectangle(10, 20)
print(f"Addition: {result}")
print(f"Area: {rect.area()}")
Output
Addition: 15
Area: 200
3. Simplifying Imports with __init__.py
In the example above, you had to be aware of the internal module names basic and geometry when using them. By editing __init__.py, you can control “what features to expose” to the outside of the package and simplify imports.
math_tools/__init__.py
# Import specific functions or classes from modules within the package
# directly under the package
from .basic import add
from .geometry import Rectangle
# 'subtract' is not exposed (by design)
By writing this in __init__.py, the user can skip the module name and import functions or classes directly from the package.
main.py (Improved Version)
# Can read directly from the package
import math_tools
# Or: from math_tools import add, Rectangle
# Accessible as math_tools.add()
# (Instead of math_tools.basic.add)
result = math_tools.add(5, 8)
rect = math_tools.Rectangle(4, 5)
print(f"Addition (Short): {result}")
print(f"Area (Short): {rect.area()}")
Output:
Addition (Short): 13
Area (Short): 20
__init__.py acts as a “reception desk,” hiding (encapsulating) the existence of internal basic.py and geometry.py from the user, allowing you to design a library that is easier to use.
Summary
- You can create a “Package” by organizing multiple modules into a directory.
- It is common to place
__init__.pyin the directory. - Use it via
from package import module. - By writing
from .module import featurein__init__.py, you can expose features directly under the package and shorten the import statement for the user.
