TL;DR: how to use type(self)
in the decorator of a member function?
I would like to do serialization of derived classes and share some serialization logic in the base class in Python.
Since pickle
and simple yaml
did not seem to be able to deal with this reliably, I then stumbled over camel
which I consider a quite neat solution to the problem see this link.
Consider two extremely simplified classes B
and A
where B
is inheriting from A
. I want to be able to serialize B
in my main function like this:
from camel import Camel, CamelRegistry
serializable_types = CamelRegistry()
# ... define A and B with dump and load functions ...
if __name__ == "__main__":
serialization_interface = Camel([serializable_types])
b = B(x=3, y=4)
s = serialization_interface.dump(b)
print(s)
I came up with two solutions that work:
Version 1: the dumping and loading is done in stand-alone functions outside of the class. Problems: not very elegant, function dumpA
not automatically available to inheriting class in dumpB
, more cumbersome function naming, function scope bigger than necessary
# VERSION 1 - dump and load in external functions
class A:
def __init__(self, x):
self._x = x
@serializable_types.dumper(A, 'object_A', version=None)
def dumpA(a):
return {'x': a._x}
@serializable_types.loader('object_A', version=None)
def loadA(data, version):
return A(data.x)
class B(A):
def __init__(self, x, y):
super().__init__(x)
self._y = y
@serializable_types.dumper(B, 'object_B', version=None)
def dumpB(b):
b_data = dumpA(b)
b_data.update({'y': b._y})
return b_data
@serializable_types.loader('object_B', version=None)
def loadB(data, version):
return B(data.x)
Version 2: functions for loading and dumping are defined directly in the constructor. Function are still not available in the subclass :/
# VERSION 2 - dump and load functions defined in constructor
class A:
def __init__(self, x):
self._x = x
@serializable_types.dumper(A, 'object_A', version=None)
def dump(a):
a.to_dict()
@serializable_types.loader('object_A', version=None)
def load(data, version):
return A(data.x)
def to_dict(self):
return {'x': self._x}
class B(A):
def __init__(self, x, y):
super().__init__(x)
self._y = y
@serializable_types.dumper(B, 'object_B', version=None)
def dump(b):
b_data = b.to_dict()
return b_data
@serializable_types.loader('object_B', version=None)
def load(data, version):
return B(data.x)
def to_dict(self):
b_data = super().to_dict()
b_data.update({'y': b._y})
return b_data
I would like to achieve an implementation that looks like this:
# VERSION 3 - dump and load functions are member functions
# ERROR: name 'A' is not defined
class A:
def __init__(self, x):
self._x = x
@serializable_types.dumper(A, 'object_A', version=None)
def dump(a):
return {'x': a._x}
@serializable_types.loader('object_A', version=None)
def load(data, version):
return A(data.x)
class B(A):
def __init__(self, x, y):
super().__init__(x)
self._y = y
@serializable_types.dumper(B, 'object_B', version=None)
def dump(b):
b_data = super().dump(b)
b_data.update({'y': b._y})
return b_data
@serializable_types.loader('object_B', version=None)
def load(data, version):
return B(data.x)
This will not work cause in the definition of the dump
functions, A
and B
are not defined. From a software design perspective however, I consider this to be the cleanest solution with fewest lines of code.
Is there a way to get the type definitions of A
and B
to work in the decorator? Or has anyone solved the problem in a different way?
I came across this but couldn't see a straightforward way of applying it to my usecase.