Ojasa Mirai

Ojasa Mirai

Python

Loading...

Learning Level

🟢 BeginneršŸ”µ Advanced
Modules Import BasicsCreating ModulesImport StatementsRelative ImportsImport PathsPackages StructureNamespace PackagesPip & DependenciesModule Performance
Python/Modules Packages/Packages Structure

šŸ“¦ Packages Structure — Organizing Modules

A package is a directory containing Python modules and an `__init__.py` file. Packages help organize related code and avoid name conflicts.


šŸŽÆ What Makes a Package?

A package is simply a directory with an `__init__.py` file.

calculator_package/
ā”œā”€ā”€ __init__.py          # Makes this a package
ā”œā”€ā”€ basic.py
ā”œā”€ā”€ advanced.py
└── trigonometry.py
# Now you can import from the package
import calculator_package.basic
from calculator_package import advanced
from calculator_package.trigonometry import sin, cos

šŸ’” The __init__.py File

The `__init__.py` file runs when the package is imported. It can be empty or contain initialization code.

Empty __init__.py:

# calculator_package/__init__.py
# (empty file - just tells Python this is a package)

With initialization code:

# calculator_package/__init__.py

print("Calculator package loaded!")

# Initialize constants
PI = 3.14159
GOLDEN_RATIO = 1.618

# Pre-load commonly used modules
from .basic import add, subtract
from .advanced import matrix_multiply

# Define what's available with 'from package import *'
__all__ = ["add", "subtract", "matrix_multiply", "PI", "GOLDEN_RATIO"]

šŸ—ļø Simple Package Structure

geometry/
ā”œā”€ā”€ __init__.py
ā”œā”€ā”€ shapes.py
ā”œā”€ā”€ colors.py
└── utils.py

geometry/__init__.py:

"""Geometry package for shape calculations."""

from .shapes import Circle, Rectangle, Triangle
from .colors import Color

__all__ = ["Circle", "Rectangle", "Triangle", "Color"]

geometry/shapes.py:

"""Shape classes and calculations."""

from .utils import validate_positive

class Circle:
    def __init__(self, radius):
        self.radius = validate_positive(radius, "radius")

    def area(self):
        return 3.14159 * self.radius ** 2

class Rectangle:
    def __init__(self, width, height):
        self.width = validate_positive(width, "width")
        self.height = validate_positive(height, "height")

    def area(self):
        return self.width * self.height

geometry/utils.py:

"""Utility functions for geometry package."""

def validate_positive(value, name):
    if value <= 0:
        raise ValueError(f"{name} must be positive")
    return value

Using the package:

from geometry import Circle, Rectangle

circle = Circle(5)
print(circle.area())        # 78.5975

rectangle = Rectangle(4, 6)
print(rectangle.area())     # 24

šŸ“š Nested Packages (Sub-packages)

Packages can contain other packages.

university/
ā”œā”€ā”€ __init__.py
ā”œā”€ā”€ students/
│   ā”œā”€ā”€ __init__.py
│   ā”œā”€ā”€ profile.py
│   └── enrollment.py
ā”œā”€ā”€ courses/
│   ā”œā”€ā”€ __init__.py
│   ā”œā”€ā”€ catalog.py
│   └── schedule.py
└── staff/
    ā”œā”€ā”€ __init__.py
    └── faculty.py

university/__init__.py:

"""University management system."""

from . import students
from . import courses
from . import staff

__all__ = ["students", "courses", "staff"]

university/students/__init__.py:

"""Student management module."""

from .profile import StudentProfile
from .enrollment import Enrollment

__all__ = ["StudentProfile", "Enrollment"]

Using nested packages:

# Method 1: Import from subpackage
from university.students import StudentProfile
from university.courses import Course

# Method 2: Access through parent
import university
student = university.students.StudentProfile("John")

# Method 3: Direct import
from university import students
profile = students.StudentProfile("Alice")

šŸŽØ Real-World Example: Web Application

webapp/
ā”œā”€ā”€ __init__.py
ā”œā”€ā”€ config.py
ā”œā”€ā”€ models/
│   ā”œā”€ā”€ __init__.py
│   ā”œā”€ā”€ user.py
│   ā”œā”€ā”€ post.py
│   └── comment.py
ā”œā”€ā”€ views/
│   ā”œā”€ā”€ __init__.py
│   ā”œā”€ā”€ user_views.py
│   ā”œā”€ā”€ post_views.py
│   └── auth_views.py
ā”œā”€ā”€ utils/
│   ā”œā”€ā”€ __init__.py
│   ā”œā”€ā”€ validators.py
│   ā”œā”€ā”€ formatters.py
│   └── decorators.py
└── static/
    └── style.css

webapp/__init__.py:

"""Flask web application."""

from flask import Flask
from . import config

def create_app():
    app = Flask(__name__)
    app.config.from_object(config)

    # Register blueprints
    from .views import user_views, post_views
    app.register_blueprint(user_views.bp)
    app.register_blueprint(post_views.bp)

    return app

webapp/models/__init__.py:

from .user import User
from .post import Post
from .comment import Comment

__all__ = ["User", "Post", "Comment"]

webapp/models/user.py:

from ..utils import validators

class User:
    def __init__(self, username, email):
        self.username = validators.validate_username(username)
        self.email = validators.validate_email(email)

webapp/views/user_views.py:

from flask import Blueprint
from ..models import User
from ..utils import formatters

bp = Blueprint("user", __name__, url_prefix="/user")

@bp.route("/<username>")
def profile(username):
    user = User.query.filter_by(username=username).first()
    return formatters.format_user_profile(user)

šŸ“¦ Package Initialization Patterns

Pattern 1: Minimal (Empty) __init__.py

# mypackage/__init__.py
# (empty)
  • Users must use full import paths
  • Explicit and clear
from mypackage.module import function

Pattern 2: Convenience Import

# mypackage/__init__.py
from .module1 import ClassA, ClassB
from .module2 import function_c

__all__ = ["ClassA", "ClassB", "function_c"]
  • Cleaner imports for users
  • Simpler syntax
from mypackage import ClassA, function_c

Pattern 3: Lazy Import

# mypackage/__init__.py
def __getattr__(name):
    if name == "expensive_module":
        from . import expensive_module
        return expensive_module
    raise AttributeError(f"module {__name__} has no attribute {name}")
  • Imports only when needed
  • Faster initialization

šŸ”„ Package Metadata

Add metadata to your `__init__.py`:

# mypackage/__init__.py

__version__ = "1.0.0"
__author__ = "John Doe"
__email__ = "john@example.com"
__license__ = "MIT"

from .main import main_function

__all__ = ["main_function"]
import mypackage

print(mypackage.__version__)   # 1.0.0
print(mypackage.__author__)    # John Doe

āš ļø Common Structure Mistakes

āŒ Mistake 1: Missing __init__.py

mypackage/              # No __init__.py!
ā”œā”€ā”€ module1.py
└── module2.py

# This won't work as a package
import mypackage.module1  # Error!

āœ… Fix: Add __init__.py

mypackage/
ā”œā”€ā”€ __init__.py          # Now it's a package
ā”œā”€ā”€ module1.py
└── module2.py

āŒ Mistake 2: Circular Imports

# models.py
from .views import render

# views.py
from .models import User  # Circular!

āœ… Fix: Import inside function

# models.py
class User:
    def display(self):
        from .views import render  # Import when needed
        return render(self)

āŒ Mistake 3: Inconsistent Import Styles

# Bad - mixing absolute and relative inconsistently
from myapp.models import User
from .utils import helpers

āœ… Fix: Use relative imports within package

# Good - consistent relative imports
from .models import User
from .utils import helpers

šŸ”‘ Key Takeaways

  • āœ… A package is a directory with `__init__.py`
  • āœ… `__init__.py` runs when package is imported
  • āœ… Use `__init__.py` to expose public API
  • āœ… Packages can contain sub-packages (nested)
  • āœ… Use `__all__` to define what's public
  • āœ… Add metadata (__version__, etc.) in `__init__.py`
  • āœ… Always include `__init__.py` in package directories

Ready to practice? Challenges | Quiz


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep • Build Real • Verify Skills • Launch Forward

Courses

PythonFastapiReactJSCloud

Ā© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn