E.B.4 Metaprogramming

Metaprogramming means using code to organize or generate code structure. In day-to-day Python engineering, the most useful version is usually not clever magic; it is automatic registration, field validation, and reducing repeated boilerplate.
What You Need
- Python 3.10+
- No external packages
- Comfort with classes
Key Terms
- Registry: a mapping that remembers available implementations.
- Decorator registration: using a decorator to add a class or function to a registry.
- Descriptor: an object that controls attribute read/write behavior.
__set_name__: lets a descriptor know which attribute name it was assigned to.- Dynamic class: a class created at runtime, often with
type.
Run A Registry And Descriptor Example
Create metaprogramming_demo.py:
REGISTRY = {}
def register(name):
def decorator(cls):
REGISTRY[name] = cls
return cls
return decorator
@register("csv")
class CsvLoader:
def load(self):
return "csv rows"
@register("json")
class JsonLoader:
def load(self):
return "json rows"
class NonEmpty:
def __set_name__(self, owner, name):
self.private_name = "_" + name
def __get__(self, instance, owner):
if instance is None:
return self
return getattr(instance, self.private_name, None)
def __set__(self, instance, value):
if not value:
raise ValueError("name cannot be empty")
setattr(instance, self.private_name, value)
class JobConfig:
name = NonEmpty()
loader = REGISTRY["json"]()
print(loader.load())
print(sorted(REGISTRY))
config = JobConfig()
config.name = "daily-import"
print(config.name)
try:
config.name = ""
except ValueError as error:
print("error:", error)
Run it:
python metaprogramming_demo.py
Expected output:
json rows
['csv', 'json']
daily-import
error: name cannot be empty
The registry removes a manual mapping table. The descriptor keeps validation next to the field definition.
When It Is Worth Using
Metaprogramming is useful when:
- Many classes need automatic registration.
- A framework needs to discover plugins.
- Many fields share the same validation behavior.
- Configuration should generate repeated structure.
Avoid it when a normal class or dictionary is clearer.
Common Mistakes
- Using dynamic tricks just to look advanced.
- Hiding behavior so deeply that debugging becomes painful.
- Removing small, harmless repetition at the cost of readability.
Practice
Add a yaml loader and confirm sorted(REGISTRY) includes it. Then create an IntegerRange(min_value, max_value) descriptor for a retry_count field.