# IReader/IWriter are common interfaces defined by you
# which BarReader and FooWriter implement
def read_my_class_read_my_class(reader: IReader, input_stream: BinaryIO) -> MyClass:
# implement deserialization
BarReader.register(MyClass, read_my_class_read_my_class)
def write_my_class_write_my_class(writer: IWriter, output_stream: BinaryIO, instance: MyClass):
# implement serialization
FooWriter.register(MyClass, write_my_class_write_my_class)
# Note: "register" can and should be a generic method.
In a more sophisticated context you can even use a visitor pattern1 on dataclasses. Of course you need a common interface for all Readers and all Writers (likely with generic read/write methods). Note that you need to pass IReader/IWriter in case of recursive (de)serialization.
The keywordkeywords here isare: separation of concerns. And depend on abstract interfaces instead of concrete classes.
1 It's a shame that you cannot extend existing Python classes in a clean, simple, safe and non-hacky way, like in Rust.