
Python
Namespace packages (PEP 420) allow you to spread package code across multiple directories without a shared `__init__.py`. Useful for multi-team projects and plugins.
A namespace package is a package without an `__init__.py` file. Multiple locations on disk can contribute to the same package.
# Traditional package (has __init__.py)
myapp/
āāā __init__.py
āāā models.py
# Namespace package (NO __init__.py)
myapp/
āāā models.pyIn Python 3.3+, directories without `__init__.py` can be namespace packages.
# Location 1: /home/dev/mycompany/utils/
mycompany/
āāā utils/
āāā formatters.py
āāā validators.py
(no __init__.py!)
# Location 2: /home/dev/plugins/mycompany/utils/
mycompany/
āāā utils/
āāā logger.py
(no __init__.py!)
# Both contribute to the same 'mycompany.utils' namespace!# Both modules are accessible from same namespace
from mycompany.utils import formatters
from mycompany.utils import logger
# They coexist in the same namespaceRegular Package (with __init__.py):
company/
āāā __init__.py # Required!
āāā hr/
ā āāā __init__.py
ā āāā payroll.py
āāā finance/
āāā __init__.py
āāā accounting.pyimport company.hr.payroll
import company.finance.accountingNamespace Package (without __init__.py):
company/ # No __init__.py
āāā hr/ # No __init__.py
ā āāā payroll.py
āāā finance/ # No __init__.py
āāā accounting.py# Works the same way
import company.hr.payroll
import company.finance.accountingScenario: Shared Company Framework
# Location A: Team A's modules (/dev/team_a/)
company/
āāā web/
āāā handlers.py
āāā middleware.py
(no __init__.py)
# Location B: Team B's modules (/dev/team_b/)
company/
āāā web/
āāā templates.py
āāā views.py
(no __init__.py)
# Location C: Shared modules (/dev/shared/)
company/
āāā web/
āāā config.py
āāā utils.py
(no __init__.py)# Add all locations to sys.path
import sys
sys.path.extend([
"/dev/team_a",
"/dev/team_b",
"/dev/shared"
])
# Now all contribute to company.web namespace
from company.web import handlers, templates, config, utils
# All modules are accessible as if they're in one package!
print(handlers) # <module 'company.web.handlers'>
print(templates) # <module 'company.web.templates'>
print(config) # <module 'company.web.config'>Use namespace packages for:
1. Plugin systems - Plugins from different sources share same namespace
# Core application
plugins/
āāā myplugin/
āāā core.py
(no __init__.py)
# User plugins (installed later)
user_plugins/
āāā myplugin/
āāā custom.py
(no __init__.py)
# Both are myplugin namespace!2. Multi-team projects - Teams maintain separate modules in same package
# Team A location
/home/team_a/company/
āāā auth/
āāā authentication.py
# Team B location
/home/team_b/company/
āāā database/
āāā models.py
# Same company namespace, different locations3. Distributed libraries - Vendor namespace packages (like zope.*)
# Company A's library
zope/
āāā interface.py
# Company B's library (different location)
zope/
āāā component.py
# Both contribute to 'zope' namespaceDirectory structure:
app/
āāā main.py
āāā plugins/ # Base plugins location
āāā (empty, no __init__.py)
user_plugins/ # User plugins location
āāā my_plugin.pyapp/main.py:
import sys
import importlib
# Add plugin location to path
sys.path.append("./user_plugins")
def load_plugins():
"""Load all plugins from the plugins namespace."""
plugins = []
# List of plugin names to load
plugin_names = ["my_plugin", "another_plugin"]
for name in plugin_names:
try:
# Dynamic import from namespace
plugin = importlib.import_module(name)
plugins.append(plugin)
print(f"Loaded: {name}")
except ImportError:
print(f"Could not load: {name}")
return plugins
def main():
plugins = load_plugins()
for plugin in plugins:
if hasattr(plugin, "run"):
plugin.run()
if __name__ == "__main__":
main()user_plugins/my_plugin.py:
"""A user-created plugin."""
def run():
print("My plugin is running!")
def get_name():
return "My Custom Plugin"| Aspect | Regular | Namespace |
|---|---|---|
| __init__.py | Required | Not needed |
| Python 3.3+ | Yes | Yes |
| Single location | Yes | Can span multiple |
| Can init code | Yes | No |
| Simpler | Yes | More complex |
| Plugin systems | Less ideal | Ideal |
From regular to namespace:
# BEFORE: Regular package with __init__.py
myapp/
āāā __init__.py
āāā utils/
āāā __init__.py
āāā helpers.py
# AFTER: Namespace package without __init__.py
myapp/
āāā utils/
āāā helpers.pyWhat changes:
# Same import syntax, but no __init__.py initialization
from myapp.utils import helpers # Still works!But loses:
# These won't work anymore
import myapp # Can't initialize at package level
print(myapp.__version__) # No package-level variablesReady to practice? Challenges | Quiz
Resources
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