在进行数据处理任务时,我经常发现自己将一系列合成,矢量化函数等应用于某些输入可迭代的数据,以生成最终结果。理想情况下,我想要一些适用于列表和生成器的东西(除了任何其他可迭代的)。我可以想出一些构建代码来实现这一目标的方法,但我能想到的每种方式都有一种或多种方式让我觉得不洁净/不干净。我在下面概述了我可以想到的不同方法,但我的问题是 - 是否有推荐的,惯用的方法来做到这一点?
我能想到的方法,用一个简单的例子说明,通常代表:
result = [sum(group)
for key, group in itertools.groupby(
filter(lambda x: x <= 2, [x **2 for x in input]),
keyfunc=lambda x: x % 3)]
对于任何非平凡的步骤序列,这通常很难阅读。在阅读代码时,也会以相反的顺序遇到每个步骤。
squared = [x**2 for x in input]
filtered = filter(lambda x: x < 2, squared)
grouped = itertools.groupby(filtered, keyfunc=lambda x: x % 3)
result = [sum(group) for key, group in grouped]
这引入了许多局部变量,这些变量通常难以描述性地描述;另外,如果某些或所有中间步骤的结果特别大,那么保持它们可能会非常浪费内存。如果想要为此过程添加一个步骤,必须注意所有变量名称都要正确更新 - 例如,如果我们希望将每个数字除以2,我们将添加行halved = [x / 2.0 for x in filtered]
,但也会有要记住在以下行中将filtered
更改为halved
。
tmp = [x**2 for x in input]
tmp = filter(lambda x: x < 2, tmp)
tmp = itertools.groupby(tmp, keyfunc=lambda x: x % 3)
result = [sum(group) for key, group in tmp]
我认为这对我来说似乎是这些选项中最不好的选择,但是将内容存储在一个通常命名的占位符变量中对我来说感觉不合理,让我怀疑有更好的方法。
答案 0 :(得分:1)
代码审核通常是样式问题的更好地方。 SO更适合解决问题。但CR可能对这个例子的完整性很挑剔。
但我可以提出一些意见:
如果你将这个计算包装在一个函数中,命名就不是很重要。这些名称不一定具有全球意义。
你的一些表达式是生成器。 Itertools倾向于生产发电机或发电机。表达式。因此,记忆的使用不应该成为一个问题。
def better_name(input):
squared = (x**2 for x in input) # gen expression
filtered = filter(lambda x: x < 2, squared)
grouped = itertools.groupby(filtered, lambda x: x % 3)
result = (sum(group) for key, group in grouped)
return result
list(better_name(input))
使用def
函数而不是lambdas也可以使代码更清晰。有一个权衡。你的lambda很简单,我可能会保留它们。
您的第二个选项比第一个选项更具可读性。表达的顺序指导我的阅读和心理评估。在第一次,很难确定最内层或第一次评估。 groupby
是一项复杂的操作,因此欢迎任何划分行动的帮助。
遵循filter
文档,这些文档是等效的:
filtered = filter(lambda x: x < 2, squared)
filtered = (x for x in squared if x<2)
我错过了return
。该函数可以返回我显示的生成器或评估列表。
groupby
keyfunc
不是关键字参数,而是位置参数。
groupby
是复杂的功能。它返回一个生成元组的生成器,其元素是生成器本身。返回这一点会更明显。
((key, list(group)) for key, group in grouped)
因此需要明确其使用的代码风格。