Class Meta is a ubiquitous Python pattern that’s often used to define class-specific attributes in a framework.
For example, Django models use the Meta pattern to store model-specific attributes, and django-rest-framework uses it to specify serializer options.
This is a powerful pattern, but parsing options can get redundant. I was recently prototyping a system and found myself spending more type writing type-checks and option validation than I did actually solving the problem at hand. This inspired me to come up with a simple design pattern to reduce complexity. This pattern leverages Python’s new excellent dataclass
construct.
Here’s an example:
class Model:
@dataclass
class MetaParser:
quantity: int=0
translations: field(default_factory=dict)
class Meta:
quantity = 123
translations = { 'foo': 'bar' }
def __init__(self):
self.meta = None
if hasattr(self, 'Meta'):
data = {
k: v for k, v in self.Meta.__dict__.items()
if not k.startswith('__') and not callable(k)
}
self.meta = self.MetaParser(**data)
What this does:
quantity
was a string, your program would throw a TypeError
.class Meta
block, the program would also throw a TypeError
.All this in ~10 lines of code. This pattern is especially useful when dealing with tall inheritance chains and/or metaclasses.