I am trying to write a metaclass to assist in serialization. The intention of the metaclass was to isolate the production code from the serialization mode (e.g. YAML or JSON) as much as possible. So, things could inherit from the class Serializable
, and not have to worry (too much) about whether it was going to be serialized as YAML or JSON.
I have the following code (for a YAML serializer). It is basically working, but I wanted to specify the Loader and Dumper as Keyword argument (since PyYaml could use SafeLoader, FullLoader, etc.) This is where I have the problem. I added keyword arguments for Loader and Dumper to the Serializable
class. These work for that class, but not for subclasses, e.g. when I define the class A
(which is a subclass of Serializable
) the meteclass’s __new__
does not get any keyword arguments.
I’m new to Python metaclasses – what am I doing wrong? Thanks.
import yaml
class SerializableMeta(type):
@classmethod
def __prepare__(cls, name, bases, **kwargs):
return {"yaml_tag": None, "to_yaml": None, "from_yaml": None}
@staticmethod
def __new__(cls, name, bases, namespace, **kwargs):
yaml_tag = f"!{name}"
cls_ = super().__new__(cls, name, bases, namespace)
cls_.yaml_tag = yaml_tag
cls_.to_yaml = lambda dumper, obj: dumper.represent_mapping(yaml_tag, obj.__dict__)
cls_.from_yaml = lambda loader, node: cls_(**loader.construct_mapping(node))
kwargs["Dumper"].add_representer(cls_, cls_.to_yaml)
kwargs["Loader"].add_constructor(yaml_tag, cls_.from_yaml)
return cls_
class Serializable(metaclass=SerializableMeta, Dumper=yaml.Dumper, Loader=yaml.Loader):
pass
class A(Serializable):
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return f"A({self.a}, {self.b})"
def __eq__(self, other):
if isinstance(other, A):
return self.a == other.a and self.b == other.b
else:
return NotImplemented
You don’t need to use @staticmethod
decorator for __new__
method, try to remove it.