我有一个列表理解,近似为:
[f(x) for x in l if f(x)]
其中l是列表,而f(x)是返回列表的昂贵函数。
我想避免对f(x)的每个非空出现两次评估f(x)。有什么方法可以将其输出保存在列表推导中?
我可以删除最终条件,生成整个列表,然后修剪它,但这似乎很浪费。
编辑 :
建议了两种基本方法:
内部生成器理解:
[y for y in (f(x) for x in l) if y]
或备忘录。
我认为内部生成器理解可以很好地解决上述问题。实际上,我简化了这个问题以使其清楚,我确实想要:
[g(x, f(x)) for x in l if f(x)]
对于这种更复杂的情况,我认为备忘录可以产生更干净的最终结果。
一种解决方案(最好是如果您重复x的话最好)是 记住 函数f,即创建一个包装函数,以保存调用函数的参数并将其保存,然后在询问相同值时返回它。
一个非常简单的实现如下:
storage = {} def memoized(value): if value not in storage: storage[value] = f(value) return storage[value] [memoized(x) for x in l if memoized(x)]
然后在列表推导中使用此功能。这种方法在两个条件下有效,一个是理论上的,一个是实践上的。第一个是函数 f 应该是确定性的,即在给定相同输入的情况下返回相同的结果,另一个是对象 x 可以用作字典键。如果第一个无效,则应根据每次定义重新计算f,而如果第二个失败,则可以使用一些更可靠的方法。
您可以在网上找到很多备忘录的实现,而且我认为新版本的python也包含一些内容。
附带说明一下,永远不要将小L用作变量名,这是一个坏习惯,因为它在某些终端上可能会与i或1混淆。
编辑:
如前所述,使用生成器理解(以避免创建无用的重复临时对象)的可能解决方案将是以下表达式:
[g(x, fx) for x, fx in ((x,f(x)) for x in l) if fx]
给定f的计算成本,原始列表中的重复次数和处置时的内存,您需要权衡选择。记忆化在空间速度上进行了折衷,这意味着它会跟踪保存每个结果的方式,因此,如果您有大量列表,则在内存占用方面可能会变得代价高昂。