In Python, to register classes in files inside packages using decorators without explicitly loading the packages, you can use a combination of decorators and a registry to achieve this. Here’s a step-by-step guide:

  1. Create a registry to keep track of your classes:
class Registry:
	def __init__(self):
		self._registry = {}
 
	def register(self, name):
		def decorator(cls):
			self._registry[name] = cls
			return cls
		return decorator
 
	def get(self, name):
		return self._registry.get(name)
 
	def all(self):
		return self._registry
  1. Instantiate the registry:
registry = Registry()
  1. Create a decorator function to register classes:
def register_class(name):
	return registry.register(name)
  1. Apply the decorator to your classes:
# mypackage/mymodule.py
from path.to.registry import register_class
 
@register_class('MyClass')
class MyClass:
	def __init__(self):
		print('MyClass instance created')
  1. Automatically load modules to ensure decorators are executed:

To automatically load modules in a package and ensure that the decorators are executed, you can dynamically import all modules in the package. This can be done using the importlib library.

import importlib
import pkgutil
import mypackage  # Replace with your package name
 
def load_modules(package):
	package_name = package.__name__
	for _, module_name, _ in pkgutil.iter_modules(package.__path__):
		importlib.import_module(f"{package_name}.{module_name}")
 
load_modules(mypackage)
  1. Access your registered classes:
# After loading modules
print(registry.all())

Full Example

  1. Registry Module (registry.py):
class Registry:
	def __init__(self):
		self._registry = {}
 
	def register(self, name):
		def decorator(cls):
			self._registry[name] = cls
			return cls
		return decorator
 
	def get(self, name):
		return self._registry.get(name)
 
	def all(self):
		return self._registry
 
registry = Registry()
 
def register_class(name):
	return registry.register(name)
  1. Module with Class (mypackage/mymodule.py):
from path.to.registry import register_class
 
@register_class('MyClass')
class MyClass:
	def __init__(self):
		print('MyClass instance created')
  1. Main Script to Load Modules and Access Registry:
import importlib
import pkgutil
import mypackage  # Replace with your package name
from path.to.registry import registry
 
def load_modules(package):
	package_name = package.__name__
	for _, module_name, _ in pkgutil.iter_modules(package.__path__):
		importlib.import_module(f"{package_name}.{module_name}")
 
load_modules(mypackage)
 
# Accessing the registry
for name, cls in registry.all().items():
	print(f"Registered class: {name} -> {cls}")

This setup ensures that by just adding the decorators to your classes and dynamically loading the modules, the classes get registered without the need for explicit imports in your main script.