\[ [Index](index.md) | [Exercise 3.4](ex3_4.md) | [Exercise 3.6](ex3_6.md) \] # Exercise 3.5 *Objectives:* - Learn how to use inheritance to write extensible code. - See a practical use of inheritance by writing a program that must output data in a variety of user-selectable formats such as plain-text, CSV, and HTML. *Files Modified:* `tableformat.py` One major use of classes in Python is in writing code that be extended/adapted in various ways. To illustrate, in [Exercise 3.2](ex3_2.md) you created a function `print_table()` that made tables. You used this to make output from the `portfolio` list. For example: ```python >>> import stock >>> import reader >>> import tableformat >>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock) >>> tableformat.print_table(portfolio, ['name','shares','price']) name shares price ---------- ---------- ---------- AA 100 32.2 IBM 50 91.1 CAT 150 83.44 MSFT 200 51.23 GE 95 40.37 MSFT 50 65.1 IBM 100 70.44 >>> ``` Suppose you wanted the `print_table()` function to be able to make tables in any number of output formats such as CSV, XML, HTML, Excel, etc. Trying to modify the function to support all of those output formats at once would be painful. A better way to do this involves moving the output-related formatting code to a class and using inheritance to implement different output formats. ## (a) Defining a generic formatter class Add the following class definition to the `tableformat.py` file: ```python class TableFormatter: def headings(self, headers): raise NotImplementedError() def row(self, rowdata): raise NotImplementedError() ``` Now, modify the `print_table()` function so that it accepts a `TableFormatter` instance and invokes methods on it to produce output: ```python def print_table(records, fields, formatter): formatter.headings(fields) for r in records: rowdata = [getattr(r, fieldname) for fieldname in fields] formatter.row(rowdata) ``` These two classes are meant to be used together. For example: ``` >>> import stock, reader, tableformat >>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock) >>> formatter = tableformat.TableFormatter() >>> tableformat.print_table(portfolio, ['name', 'shares', 'price'], formatter) Traceback (most recent call last): ... NotImplementedError >>> ``` For now, it doesn't do much of anything interesting. You'll fix this in the next section. ## (b) Implementing a concrete formatter The `TableFormatter` isn't meant to be used by itself. Instead, it is merely a base for other classes that will implement the formatting. Add the following class to `tableformat.py`: ```python class TextTableFormatter(TableFormatter): def headings(self, headers): print(' '.join('%10s' % h for h in headers)) print(('-'*10 + ' ')*len(headers)) def row(self, rowdata): print(' '.join('%10s' % d for d in rowdata)) ``` Now, use your new class as follows: ```python >>> import stock, reader, tableformat >>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock) >>> formatter = tableformat.TextTableFormatter() >>> tableformat.print_table(portfolio, ['name','shares','price'], formatter) name shares price ---------- ---------- ---------- AA 100 32.2 IBM 50 91.1 CAT 150 83.44 MSFT 200 51.23 GE 95 40.37 MSFT 50 65.1 IBM 100 70.44 >>> ``` ## (c) Adding More Implementations Create a class `CSVTableFormatter` that allows output to be generated in CSV format: ```python >>> import stock, reader, tableformat >>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock) >>> formatter = tableformat.CSVTableFormatter() >>> tableformat.print_table(portfolio, ['name','shares','price'], formatter) name,shares,price AA,100,32.2 IBM,50,91.1 CAT,150,83.44 MSFT,200,51.23 GE,95,40.37 MSFT,50,65.1 IBM,100,70.44 >>> ``` Create a class `HTMLTableFormatter` that generates output in HTML format: ```python >>> import stock, reader, tableformat >>> portfolio = reader.read_csv_as_instances('Data/portfolio.csv', stock.Stock) >>> formatter = tableformat.HTMLTableFormatter() >>> tableformat.print_table(portfolio, ['name','shares','price'], formatter)