from abc import ABC, abstractmethod class TableFormatter(ABC): @abstractmethod def headings(self, headers): pass @abstractmethod def row(self, rowdata): pass class TextTableFormatter(TableFormatter): def _printer(self, data): print(*("{: >10}".format(value) for value in data)) def headings(self, headers): self._printer(headers) print(*("{:->10}".format("") for _ in headers)) def row(self, rowdata): self._printer(rowdata) class CSVTableFormatter(TableFormatter): def _printer(self, data): print(",".join(str(value) for value in data)) def headings(self, headers): return self._printer(headers) def row(self, rowdata): return self._printer(rowdata) class HTMLTableFormatter(TableFormatter): def _cell(self, value, tag): return f"<{tag}>{value}" def _printer(self, data, tag): line = f"{' '.join(self._cell(str(value), tag) for value in data)}" print(line) def headings(self, headers): return self._printer(headers, "th") def row(self, rowdata): return self._printer(rowdata, "td") def create_formatter(name): formatters = { "text": TextTableFormatter, "csv": CSVTableFormatter, "html": HTMLTableFormatter, } formatter = formatters.get(name, None) if not name: raise ValueError(f'formatter named "{name}" not implemented') return formatter() def print_table(records, fields, formatter): if not isinstance(formatter, TableFormatter): raise TypeError("expected a TableFormatter") formatter.headings(fields) for record in records: formatter.row([getattr(record, fieldname) for fieldname in fields])