113 lines
3.2 KiB
Markdown
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]]
|