Python编程设计讨论
设计讨论
本节将重新评估之前的某些设计决策。
文件名与可迭代对象
考虑以下两个返回相同结果的函数:
# 提供文件名
def 读取数据(文件名):
记录 = []
with open(文件名) as 文件:
for 行 in 文件:
...
记录.append(条目)
return 记录
数据 = 读取数据('文件.csv')
# 提供行
def 读取数据(行):
记录 = []
for 行 in 行:
...
记录.append(条目)
return 记录
with open('文件.csv') as 文件:
数据 = 读取数据(文件)
- 你更喜欢哪个函数?为什么?
- 哪个函数更灵活?
鸭子类型(Duck Typing)
鸭子类型是一种编程概念,用于判断一个对象是否适合某种用途。其核心思想是"如果它走起来像鸭子,叫起来像鸭子,那么它可能就是一只鸭子。"
第二个`读取数据()`函数可以接受任何可迭代对象,而不仅仅是文件行:
def 读取数据(行):
记录 = []
for 行 in 行:
...
记录.append(条目)
return 记录
这使得我们可以处理其他类型的"行":
# CSV文件
行 = open('数据.csv')
数据 = 读取数据(行)
# 压缩文件
行 = gzip.open('数据.csv.gz', 'rt')
数据 = 读取数据(行)
# 标准输入
行 = sys.stdin
数据 = 读取数据(行)
# 字符串列表
行 = ['ACME,50,91.1', 'IBM,75,123.45', ...]
数据 = 读取数据(行)
这种设计非常灵活。
问题:我们应该支持这种灵活性吗?
库设计最佳实践
通常,支持灵活性可以使代码库更加有用。不要限制选择,灵活性越大,功能越强大。
练习
练习 3.17:从文件名到类文件对象
你已经创建了一个包含`解析_csv()`函数的`文件解析.py`文件。该函数的工作方式如下:
>>> import 文件解析
>>> 股票组合 = 文件解析.解析_csv('数据/股票组合.csv', 类型=[str, int, float])
>>>
虽然该函数接受文件名作为参数,但可以使其更加灵活。请修改该函数,使其可以接受任何类文件对象或可迭代对象。例如:
>>> import 文件解析
>>> import gzip
>>> with gzip.open('数据/股票组合.csv.gz', 'rt') as 文件:
... 组合 = 文件解析.解析_csv(文件, 类型=[str, int, float])
...
>>> 行 = ['名称,份额,价格', 'AA,100,34.23', 'IBM,50,91.1', 'HPE,75,45.1']
>>> 组合 = 文件解析.解析_csv(行, 类型=[str, int, float])
>>>
如果像以前一样传递文件名会发生什么?
>>> 组合 = 文件解析.解析_csv('数据/股票组合.csv', 类型=[str, int, float])
>>> 组合
... 查看输出(应该是错误的)...
>>>
如上所示,这可能导致意外结果,因此修改时需要小心。可以添加安全检查以防止这种情况吗?
练习 3.18:修复现有函数
请修复`报告.py`文件中的`读取股票组合()`和`读取价格()`函数,使其可以使用修改后的`解析_csv()`函数。这只需要少量修改。之后,`报告.py`和`总成本.py`程序应能正常运行。