目录
- 问题
- 解决方案
- 讨论
问题
当换算(sum(), min(), max())同时遇到转换或筛选,怎么做?
解决方案
有一种非常优雅的方法,可以将数据换算和转换结合在一起——在函数参数中使用 生成器表达式。
一个直观的案例:
nums = [1, 2, 3, 4, 5]
s = sum(x * x for x in nums)
print(s)
还有一些其他的例子:
import os
files = os.listdir('dirname')
if any(name.endswith('.py') for name in files):
print("There be python!")
else:
print("Sorry, no python.")
s = ('ACME', 50, 123.45)
print(','.join(str(x) for x in s))
portfolio = [
{'name': 'GOOG', 'shares': 50},
{'name': 'YHOO', 'shares': 75},
{'name': 'AOL', 'shares': 20},
{'name': 'SCOX', 'shares': 65}
]
min_shares = min(s['shares'] for s in portfolio)
print(min_shares)
讨论
这种同时对数据做转换和换算的方案,解决了当把生成器作为函数的单独参数时在语法上的一些微妙之处,即,不必重复使用括号:
# 上下两行代码同意:
s = sum([x * x for x in nums])
s = sum(x * x for x in nums)
比起首先创建一个临时的列表,当然使用生成器做参数更为高效和优雅。
nums = [1, 2, 3, 4, 5]
print(sum([x * x for x in nums]))
print(sum(x * x for x in nums))
看似上述两个没差别,都能实现最终结果,但是细想,第一个方法引入了一个额外的列表。对于很小的 nums 可能无关紧要,但是如果 nums 数量非常大,那么就会创建一个庞大的临时数据结构,而且只用一次就丢弃。基于生成器的解决方案可以以迭代的方式转换数据,内存友好高效。
最后,我们可以完善我们的输出:
min_shares = min(s['shares'] for s in portfolio)
print(min_shares)
min_shares = min(portfolio, key=lambda s: s['shares'])
print(min_shares)