Skip to main content

Command Palette

Search for a command to run...

🐍 Python Modules, Packages & Namespaces: The Complete Guide

Published
4 min read
🐍 Python Modules, Packages & Namespaces: The Complete Guide
A

Full-Stack Developer & Tech Writer specializing in Python (Django, FastAPI, Flask) and JavaScript (React, Next.js, Node.js). I build fast, scalable web apps and share practical insights on backend architecture, frontend performance, APIs, and Web3 integration. Available for freelance and remote roles.

What really happens when you type import math? Let’s peel back the curtain.

If you’re learning Python, import might feel like magic. One second, nothing. The next, math.sqrt() is at your fingertips. But Python is doing a lot more behind the scenes than just handing you a function.

In this guide, we’ll explore modules, packages, and namespaces what they are, how Python handles them, and how you can use them to write cleaner, maintainable code. Expect real-world examples, behind-the-scenes insights, and some fun along the way.

🧩 What is a Module?

A module is simply a single Python file.

Example:

# greetings.py
def hello(name):
    return f"Hello, {name}!"

Use it in another file:

# app.py
import greetings

print(greetings.hello("Anik"))

Output:

Hello, Anik!

Behind the Scenes: How import Works

When Python sees import greetings:

  1. Checks if the module is already loaded in sys.modules.

  2. Searches for the module in sys.path (current directory → PYTHONPATH → standard libraries → site-packages).

  3. Loads & executes the module (creates .pyc bytecode).

  4. Caches it in sys.modules for fast future imports.

Fun fact: importing the same module multiple times doesn’t rerun the code Python just reuses the cached version.

import sys, greetings
print(sys.modules['greetings'])

🔄 __name__ The Module Identity

Every module has a built-in variable __name__.

  • Direct execution → __name__ == "__main__"

  • Imported → __name__ == "module_name"

Example:

# greetings.py
print(f"Running as {__name__}")

if __name__ == "__main__":
    print("This runs only when executing greetings.py directly.")

Run directly:

$ python greetings.py
Running as __main__
This runs only when executing greetings.py directly.

Import it:

>>> import greetings
Running as greetings

This allows libraries to be importable modules and CLI-ready scripts at the same time.

🏗 Real-World Example: A Modular Calculator

Instead of one giant script, organize your code:

calculator/
    __init__.py
    operations.py
    utils.py
    app.py

operations.py

def add(a, b): return a + b
def subtract(a, b): return a - b

utils.py

def format_result(value):
    return f"Result: {value}"

app.py

from operations import add
from utils import format_result

print(format_result(add(10, 5)))

Output:

Result: 15

✅ Modular code = easier maintenance + testing + scaling.

🔀 Import Variants

Python offers multiple ways to import:

import math           # Full import
import math as m      # Alias
from math import sqrt # Specific import
from math import *    # Import everything (avoid!)

Tips:

  • ✅ Prefer import x or import x as y for clarity

  • ✅ Use from x import y for a few names

  • ❌ Avoid from x import * pollutes the namespace

⚙️ Dynamic Imports

Sometimes you only know what to import at runtime, e.g., plugin systems.

import importlib

module_name = "math"
math_module = importlib.import_module(module_name)
print(math_module.sqrt(25))

This is how Django apps or pytest discover modules dynamically.

🔄 Reloading Modules

During development:

import importlib, greetings
importlib.reload(greetings)

Re-executes the module’s code. Great for REPL sessions, but beware global state may persist.

📦 Packages, Organizing Modules

A package is a folder containing modules and an optional __init__.py:

my_package/
    __init__.py
    module_a.py
    module_b.py
import my_package.module_a
from my_package import module_b

__init__.py The Gatekeeper

# __init__.py
from .module_a import function_a
__all__ = ['function_a']

Now users can simply:

from my_package import function_a

🧩 Namespace Packages

Need multiple teams to contribute to the same package from different repos?

repo1/mypackage/a.py
repo2/mypackage/b.py

Add both folders to sys.path:

import mypackage.a, mypackage.b

This is how Google Cloud Python libraries handle distributed contributions.

🗂 Tips for Structuring Packages

  • Group related code logically

  • Keep __init__.py clean

  • Avoid circular imports (local imports if needed)

  • Use relative imports inside packages (from .module import func)

📦 Bonus: Import from Zip Archives

Python can import directly from .zip files:

import sys
sys.path.append('my_modules.zip')

import some_module

Useful for self-contained apps or plugins.

🧠 Bytecode & Caching

Python caches compiled .pyc files in __pycache__/ for faster imports.

Peek at bytecode with dis:

import dis, greetings
dis.dis(greetings.hello)

It’s like peeking inside Python’s brain 🧠.

🏆 Key Takeaways

  • Modules = .py files; Packages = directories of modules

  • Imports are cached in sys.modules

  • __name__ == "__main__" allows dual-purpose scripts

  • importlib = dynamic imports & reloads

  • Namespace packages = distributed development possible

  • Python can import from folders, zip files, or remote paths

🎉 Next time you type import math, remember Python is quietly running a whole orchestration of caching, bytecode, and namespace magic behind the scenes.

More from this blog

A

Anik Sikder - The Dev Loop

21 posts

Practical tips on building fast, scalable web apps using Python, JavaScript, and Web3. Learn how I turn ideas into reliable, production-ready digital products.