简介
在本节中,我们重新审视之前做出的一个设计决策。
这是一个实验(Guided Lab),提供逐步指导来帮助你学习和实践。请仔细按照说明完成每个步骤,获得实际操作经验。根据历史数据,这是一个 初级 级别的实验,完成率为 83%。获得了学习者 100% 的好评率。
文件名与可迭代对象
比较这两个返回相同输出的程序。
## 提供一个文件名
def read_data(filename):
records = []
with open(filename) as f:
for line in f:
...
records.append(r)
return records
d = read_data('file.csv')
## 提供各行内容
def read_data(lines):
records = []
for line in lines:
...
records.append(r)
return records
with open('file.csv') as f:
d = read_data(f)
- 你更喜欢这两个函数中的哪一个?为什么?
- 这两个函数中哪一个更灵活?
深度理解:“鸭子类型”
鸭子类型 是一种计算机编程概念,用于确定一个对象是否可用于特定目的。它是 鸭子测试 的一种应用。
如果它看起来像鸭子,游泳方式像鸭子,叫声也像鸭子,那么它很可能就是一只鸭子。
在上述 read_data() 的第二个版本中,该函数期望的是任何可迭代对象,而不仅仅是文件的各行。
def read_data(lines):
records = []
for line in lines:
...
records.append(r)
return records
这意味着我们可以将它用于其他的“行”。
## 一个 CSV 文件
lines = open('data.csv')
data = read_data(lines)
## 一个压缩文件
lines = gzip.open('data.csv.gz','rt')
data = read_data(lines)
## 标准输入
lines = sys.stdin
data = read_data(lines)
## 一个字符串列表
lines = ['ACME,50,91.1','IBM,75,123.45',... ]
data = read_data(lines)
这种设计具有相当大的灵活性。
问题:我们应该接受还是抵制这种灵活性?
库设计的最佳实践
代码库通常通过接受灵活性来更好地发挥作用。不要限制你的选择。强大的灵活性带来强大的能力。
练习 3.17:从文件名到类文件对象
你现在已经创建了一个名为 fileparse.py 的文件,其中包含一个函数 parse_csv()。该函数的工作方式如下:
>>> import fileparse
>>> portfolio = fileparse.parse_csv('portfolio.csv', types=[str,int,float])
>>>
目前,该函数期望传入一个文件名。然而,你可以让代码更灵活。修改该函数,使其能与任何类文件/可迭代对象一起工作。例如:
>>> import fileparse
>>> import gzip
>>> with gzip.open('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])
>>>
在这段新代码中,如果你像之前那样传入一个文件名会发生什么?
>>> port = fileparse.parse_csv('portfolio.csv', types=[str,int,float])
>>> port
... 查看输出(应该会很奇怪)...
>>>
是的,你需要小心。你能添加一个安全检查来避免这种情况吗?
练习 3.18:修复现有函数
修复 report.py 文件中的 read_portfolio() 和 read_prices() 函数,使其能与修改后的 parse_csv() 版本一起工作。这应该只涉及一个小修改。之后,你的 report.py 和 pcost.py 程序应该能像以前一样正常工作。
总结
恭喜你!你已经完成了“设计讨论”实验。你可以在 LabEx 中练习更多实验来提升你的技能。