Files

113 lines
3.2 KiB
Markdown

---
id: practical-python-4.5
source_exercise_id: "4.5"
title: "An Extensibility Problem"
section: "4.2 Inheritance"
source_path: "04_Classes_objects/02_Inheritance.md"
source_repo: "https://github.com/dabeaz-course/practical-python"
source_commit: "93dca856b41c61a0a0f85ae334116e4c125629ea"
student_visible_solution: false
has_private_solution: false
skip: false
---
# Exercise 4.5: An Extensibility Problem
> Source: Practical Python Programming, `04_Classes_objects/02_Inheritance.md`.
### Exercise 4.5: An Extensibility Problem
Suppose that you wanted to modify the `print_report()` function to
support a variety of different output formats such as plain-text,
HTML, CSV, or XML. To do this, you could try to write one gigantic
function that did everything. However, doing so would likely lead to
an unmaintainable mess. Instead, this is a perfect opportunity to use
inheritance instead.
To start, focus on the steps that are involved in a creating a table.
At the top of the table is a set of table headers. After that, rows
of table data appear. Let's take those steps and put them into
their own class. Create a file called `tableformat.py` and define the
following class:
```python
# tableformat.py
class TableFormatter:
def headings(self, headers):
'''
Emit the table headings.
'''
raise NotImplementedError()
def row(self, rowdata):
'''
Emit a single row of table data.
'''
raise NotImplementedError()
```
This class does nothing, but it serves as a kind of design specification for
additional classes that will be defined shortly. A class like this is
sometimes called an "abstract base class."
Modify the `print_report()` function so that it accepts a
`TableFormatter` object as input and invokes methods on it to produce
the output. For example, like this:
```python
# report.py
...
def print_report(reportdata, formatter):
'''
Print a nicely formatted table from a list of (name, shares, price, change) tuples.
'''
formatter.headings(['Name','Shares','Price','Change'])
for name, shares, price, change in reportdata:
rowdata = [ name, str(shares), f'{price:0.2f}', f'{change:0.2f}' ]
formatter.row(rowdata)
```
Since you added an argument to print_report(), you're going to need to modify the
`portfolio_report()` function as well. Change it so that it creates a `TableFormatter`
like this:
```python
# report.py
import tableformat
...
def portfolio_report(portfoliofile, pricefile):
'''
Make a stock report given portfolio and price data files.
'''
# Read data files
portfolio = read_portfolio(portfoliofile)
prices = read_prices(pricefile)
# Create the report data
report = make_report_data(portfolio, prices)
# Print it out
formatter = tableformat.TableFormatter()
print_report(report, formatter)
```
Run this new code:
```python
>>> ================================ RESTART ================================
>>> import report
>>> report.portfolio_report('Data/portfolio.csv', 'Data/prices.csv')
... crashes ...
```
It should immediately crash with a `NotImplementedError` exception. That's not
too exciting, but it's exactly what we expected. Continue to the next part.
## 关联来源
- [[summaries/02_Inheritance]]