Packages
A package is a way of organizing related modules into a single directory hierarchy. Packages help you organize large codebases and create reusable libraries.
What is a Package?
A package is a directory containing:
- Python modules (
.pyfiles) - An
__init__.pyfile (makes the directory a package) - Optionally, subpackages (nested directories with their own
__init__.py)
Package Structure
Here’s a typical package structure:
my_package/ __init__.py module1.py module2.py subpackage/ __init__.py module3.py module4.pyThe __init__.py File
The __init__.py file makes a directory a Python package. It can be:
- Empty (just marks the directory as a package)
- Contains initialization code
- Controls what gets imported with
from package import *
Empty __init__.py
# __init__.py# Empty file - just marks directory as package__init__.py with Initialization
# __init__.py"""My Package - A collection of useful utilities."""
# Import commonly used itemsfrom .module1 import function1, function2from .module2 import Class1
# Package-level variables__version__ = "1.0.0"__author__ = "Your Name"
# Define what gets imported with *__all__ = ['function1', 'function2', 'Class1']Creating a Simple Package
Let’s create a simple package step by step:
Step 1: Create Directory Structure
calculator/ __init__.py basic.py advanced.pyStep 2: Create Modules
# calculator/basic.pydef add(a, b): return a + b
def subtract(a, b): return a - b
def multiply(a, b): return a * b
def divide(a, b): if b == 0: raise ValueError("Cannot divide by zero") return a / b# calculator/advanced.pyimport math
def power(a, b): return a ** b
def sqrt(a): return math.sqrt(a)
def factorial(n): return math.factorial(n)Step 3: Create __init__.py
# calculator/__init__.py"""Calculator package for basic and advanced operations."""
from .basic import add, subtract, multiply, dividefrom .advanced import power, sqrt, factorial
__all__ = ['add', 'subtract', 'multiply', 'divide', 'power', 'sqrt', 'factorial']Step 4: Use the Package
# Import from packagefrom calculator import add, multiply, power
result1 = add(5, 3)result2 = multiply(4, 7)result3 = power(2, 8)
print(result1) # 8print(result2) # 28print(result3) # 256Package Import Methods
Method 1: Import Package
import calculator
# Access through package nameresult = calculator.basic.add(5, 3)Method 2: Import Module from Package
from calculator import basic
result = basic.add(5, 3)Method 3: Import Function from Package
from calculator import add
result = add(5, 3)Method 4: Import from Subpackage
from calculator.advanced import power
result = power(2, 8)Nested Packages (Subpackages)
You can create packages within packages:
my_package/ __init__.py utils/ __init__.py string_utils.py math_utils.py data/ __init__.py processors.py validators.pyImporting from Subpackages
from my_package.utils import string_utilsfrom my_package.data import processors
# Orfrom my_package.utils.string_utils import function_namePackage Initialization
The __init__.py file runs when the package is first imported:
# my_package/__init__.pyprint("Package is being imported!")
def initialize(): print("Package initialized")
# This runs on importinitialize()Package-Level Variables
You can define package-level variables in __init__.py:
# my_package/__init__.py__version__ = "1.0.0"__author__ = "Your Name"__license__ = "MIT"
# Access from outside# import my_package# print(my_package.__version__)Controlling Package Exports
Use __all__ to control what gets imported with from package import *:
# my_package/__init__.pyfrom .module1 import func1, func2from .module2 import Class1
# Only these will be imported with *__all__ = ['func1', 'func2', 'Class1']Package Example: Math Utilities
Let’s create a complete example:
Directory Structure
math_utils/ __init__.py basic.py geometry.py statistics.pyModule Files
# math_utils/basic.pydef add(a, b): return a + b
def multiply(a, b): return a * b# math_utils/geometry.pyimport math
def circle_area(radius): return math.pi * radius ** 2
def rectangle_area(length, width): return length * width# math_utils/statistics.pydef mean(numbers): return sum(numbers) / len(numbers)
def median(numbers): sorted_nums = sorted(numbers) n = len(sorted_nums) if n % 2 == 0: return (sorted_nums[n//2 - 1] + sorted_nums[n//2]) / 2 return sorted_nums[n//2]Package Init
# math_utils/__init__.py"""Math utilities package."""
from .basic import add, multiplyfrom .geometry import circle_area, rectangle_areafrom .statistics import mean, median
__version__ = "1.0.0"__all__ = ['add', 'multiply', 'circle_area', 'rectangle_area', 'mean', 'median']Using the Package
from math_utils import add, circle_area, mean
# Basic operationsresult = add(5, 3)print(result) # 8
# Geometryarea = circle_area(5)print(area) # 78.53981633974483
# Statisticsavg = mean([1, 2, 3, 4, 5])print(avg) # 3.0Package Best Practices
- Use
__init__.py: Always include it, even if empty - Organize logically: Group related modules together
- Document packages: Add docstrings to
__init__.py - Use
__all__: Control what gets exported - Keep it simple: Don’t create unnecessary nesting
- Version your packages: Include version info in
__init__.py
Package vs Module
- Module: A single
.pyfile - Package: A directory containing multiple modules and an
__init__.pyfile
Exercises
Exercise 1: Understanding Package Structure
Create a simple package structure. You have a package called utils with a module string_utils.py containing a function uppercase(text). Write code to import and use this function. Assume the package structure exists.
Understanding Package Structure
# Assuming utils package exists:# __init__.py# string_utils.py
# In string_utils.py:# def uppercase(text):# return text.upper()
# Import and usefrom utils.string_utils import uppercase
text = input()result = uppercase(text)print(result)Exercise 2: Package Import Methods
Given a package math_ops with module basic.py containing add(a, b), demonstrate three different ways to import and use this function. Use method 3 (direct function import) and calculate add(10, 20).
Package Import Methods
# Method 1: import package# import math_ops# result = math_ops.basic.add(10, 20)
# Method 2: import module# from math_ops import basic# result = basic.add(10, 20)
# Method 3: import function (using this)from math_ops.basic import add
result = add(10, 20)print(result)