The following code does three things:
- The filename is determined by the RawData class
- The reader and writer send data through a processing pipeline
- (Hopefully) makes changes and extensions easy
Regarding (2), the processing pipeline consists of "processors", that can process and deprocess data. For example, if you run the code below and look in ortho_animals.txt
, you'll see the JSON version of a dict with (keys, values) reversed.
I left comments out to keep the size of the code to a minimum. Please let me know what you think, with a particular eye on the class designs and interactions.
import contextlib import json class Processor(object): @classmethod def process(cls, data): raise NotImplementedError @classmethod def deprocess(cls, data): raise NotImplementedError class JsonProcessor(Processor): @classmethod def process(cls, data): return json.dumps(data) @classmethod def deprocess(cls, data): return json.loads(data) class SwapProcessor(Processor): @classmethod def process(cls, data): return cls.swap(data) @classmethod def deprocess(cls, data): return cls.swap(data) @classmethod def swap(cls, data): return {v: k for k, v in data.iteritems()} class AggregateProcessor(Processor): def __init__(self, processors): self.processors = processors def process(self, data): return reduce(lambda d, kls: kls.process(d), self.processors, data) def deprocess(self, data): return reduce(lambda d, kls: kls.deprocess(d), self.processors[::-1], data) class RawReader(object): def __init__(self, file_obj, processor): self._file = file_obj self._processor = processor def read(self): data = self._file.read() return self._processor.deprocess(data) class RawWriter(object): def __init__(self, file_obj, processor): self._file = file_obj self._processor = processor def write(self, data): new_data = self._processor.process(data) self._file.write(new_data) class RawData(object): processors = [SwapProcessor, JsonProcessor] aggregator = AggregateProcessor(processors) def __init__(self, obj_type, key): self.obj_type = obj_type self.key = key def _build_filename(self): return self.obj_type + '_' + self.key + '.txt' @contextlib.contextmanager def writer(self): filename = self._build_filename() with open(filename, 'w') as f: yield RawWriter(f, self.aggregator) @contextlib.contextmanager def reader(self): filename = self._build_filename() with open(filename, 'r') as f: yield RawReader(f, self.aggregator) if __name__ == '__main__': data = {'cats': 'suck', 'dogs': 'rule'} rd = RawData('ortho', key='animals') with rd.writer() as writer: writer.write(data) with rd.reader() as reader: new_data = reader.read() assert(new_data['cats'] == data['cats'])