我们需要调用一个换算(reduction)函数,例如 sum()、min()、max()等,但首先得对数据做转换或筛选。
解决方案
一种优雅的方式能将数据换算和转换结合在一起,即在函数中使用生成器表达式。例如,要计算平方和,可以这样:
代码语言:javascript复制nums = [1, 2, 3, 4, 5]
s = sum(x * x for x in nums)
更多的例子:
代码语言:javascript复制# Determine if any .py files exist in a directory
import os
files = os.listdir('dirname')
if any(name.endswith('.py') for name in files):
print('There be python!')
else:
print('Sorry, no python.')
# Output a tuple as CSV
s = ('ACME', 50, 123.45)
print(','.join(str(x) for x in s))
# Data reduction across fields of a data structure
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)
讨论
上述解决方案展示了当把生成器表达式作为函数的单独参数时在语法上的一些微妙之处,即不必重复使用括号。比如,下面两行代码的功能是一样的:
代码语言:javascript复制s = sum((x * x for x in nums)) # 显式的传递一个生成器表达式对象
s = sum(x * x for x in nums) # 更加优雅的实现方式,省略了括号
相对于首先创建一个列表,使用生成器做参数通常更为高效和优雅。例如,如果不使用生成器表达式,可能会写出如下代码:
代码语言:javascript复制nums = [1, 2, 3, 4, 5]
s = sum([x * x for x in nums])
这种方式同样能工作,但这引入了额外的一个列表。对于小数据集无关紧要,但是数据集比较大时,可能生成庞大的临时结构。
基于生成器的解决方案可以以迭代的方式转换数据,因此其内存使用要高效得多。
当然,某些特定的换算函数比如 min() 和 max() 都可以接受一个 key 参数。例如在 portfolio 的例子中,可以使用下面的替代方案:
代码语言:javascript复制# Original: Returns 20
min_shares = min(s['shares'] for s in portfolio)
# Alternative: Returns {'name': 'AOL', 'shares': 20}
min_shares = min(portfolio, key=lambda s: s['shares'])
参考
- 《Python Cookbook》第三版
- http://python3-cookbook.readthedocs.org/zh_CN/latest/