chore: clean repository history

This commit is contained in:
xixu-me committed 2026-06-05 21:13:11 +08:00
commit e1fb2929c0
661 files changed
+167488

No files matched your search

@@ -0,0 +1,137 @@
[Contents](../Contents.md) \| [Previous (3.5 Main module)](05_Main_module.md) \| [Next (4 Classes)](../04_Classes_objects/00_Overview.md)
# 3.6 Design Discussion
In this section we reconsider a design decision made earlier.
### Filenames versus Iterables
Compare these two programs that return the same output.
```python
# Provide a filename
def read_data(filename):
records = []
with open(filename) as f:
for line in f:
...
records.append(r)
return records
d = read_data('file.csv')
```
```python
# Provide lines
def read_data(lines):
records = []
for line in lines:
...
records.append(r)
return records
with open('file.csv') as f:
d = read_data(f)
```
* Which of these functions do you prefer? Why?
* Which of these functions is more flexible?
### Deep Idea: "Duck Typing"
[Duck Typing](https://en.wikipedia.org/wiki/Duck_typing) is a computer
programming concept to determine whether an object can be used for a
particular purpose. It is an application of the [duck
test](https://en.wikipedia.org/wiki/Duck_test).
> If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
In the second version of `read_data()` above, the function expects any
iterable object. Not just the lines of a file.
```python
def read_data(lines):
records = []
for line in lines:
...
records.append(r)
return records
```
This means that we can use it with other *lines*.
```python
# A CSV file
lines = open('data.csv')
data = read_data(lines)
# A zipped file
lines = gzip.open('data.csv.gz','rt')
data = read_data(lines)
# The Standard Input
lines = sys.stdin
data = read_data(lines)
# A list of strings
lines = ['ACME,50,91.1','IBM,75,123.45', ... ]
data = read_data(lines)
```
There is considerable flexibility with this design.
*Question: Should we embrace or fight this flexibility?*
### Library Design Best Practices
Code libraries are often better served by embracing flexibility.
Don't restrict your options. With great flexibility comes great power.
## Exercise
### Exercise 3.17: From filenames to file-like objects
You've now created a file `fileparse.py` that contained a
function `parse_csv()`. The function worked like this:
```python
>>> import fileparse
>>> portfolio = fileparse.parse_csv('Data/portfolio.csv', types=[str,int,float])
>>>
```
Right now, the function expects to be passed a filename. However, you
can make the code more flexible. Modify the function so that it works
with any file-like/iterable object. For example:
```
>>> import fileparse
>>> import gzip
>>> with gzip.open('Data/portfolio.csv.gz', 'rt') as file:
... port = fileparse.parse_csv(file, types=[str,int,float])
...
>>> lines = ['name,shares,price', 'AA,100,34.23', 'IBM,50,91.1', 'HPE,75,45.1']
>>> port = fileparse.parse_csv(lines, types=[str,int,float])
>>>
```
In this new code, what happens if you pass a filename as before?
```
>>> port = fileparse.parse_csv('Data/portfolio.csv', types=[str,int,float])
>>> port
... look at output (it should be crazy) ...
>>>
```
Yes, you'll need to be careful. Could you add a safety check to avoid this?
### Exercise 3.18: Fixing existing functions
Fix the `read_portfolio()` and `read_prices()` functions in the
`report.py` file so that they work with the modified version of
`parse_csv()`. This should only involve a minor modification.
Afterwards, your `report.py` and `pcost.py` programs should work
the same way they always did.
[Contents](../Contents.md) \| [Previous (3.5 Main module)](05_Main_module.md) \| [Next (4 Classes)](../04_Classes_objects/00_Overview.md)