我有一个列表理解这近似于:
[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)]
对于这种更复杂的情况,我觉得记忆化生产更清洁的最终结果。
Answer 1:
将(最好的,如果你有反复x的值)将是memoize的函数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各自timeby定义,如果第二个出现故障,可以使用一些稍微更稳健的做法,同时。
你可以找到很多围绕实现净记忆化的,而且我认为蟒蛇的新版本已经包含在他们的东西太多。
在一个侧面说明,千万不要用小L,以一个变量名,是因为它可以与我或某些终端1相混淆一个坏习惯。
编辑:
作为评价,使用发电机理解(以避免创建无用重复临时量)一个可能的解决方案将是这样的表达式:
[g(x, fx) for x, fx in ((x,f(x)) for x in l) if fx]
你需要给f的计算成本,重复在你处置原始列表和内存的数量与重量的选择。 记忆化使空间和速度的权衡,这意味着它保持每个结果的曲目保存,所以如果你有庞大的名单也可以成为对内存的占用前昂贵。
Answer 2:
[y for y in (f(x) for x in l) if y]
会做。
Answer 3:
您应该使用memoize的装饰。 这里有一个有趣的链接 。
从链接和你的“代码”使用记忆化:
def memoize(f):
""" Memoization decorator for functions taking one or more arguments. """
class memodict(dict):
def __init__(self, f):
self.f = f
def __call__(self, *args):
return self[args]
def __missing__(self, key):
ret = self[key] = self.f(*key)
return ret
return memodict(f)
@memoize
def f(x):
# your code
[f(x) for x in l if f(x)]
Answer 4:
[y for y in [f(x) for x in l] if y]
对于更新的问题,这可能是有用的:
[g(x,y) for x in l for y in [f(x)] if y]
Answer 5:
不。 有没有( 干净 )的方式来做到这一点。 这没有什么错配好,老式的循环:
output = []
for x in l:
result = f(x)
if result:
output.append(result)
如果您发现难以阅读,你可以随时把它包装在一个函数。
Answer 6:
由于以前的答案表明,你可以使用双理解或使用记忆化。 对于合理大小的问题,这是一个品味的问题(我同意,记忆化看起来更清洁,因为它隐藏了优化)。 但是,如果您正在检查一个非常大名单, 有一个巨大的差别:记忆化将存储您计算出每一个值,并能迅速吹出来的你的记忆。 与发电机的双修真(圆括号,而不是方括号中)仅存储您要保留的。
来到您的实际问题:
[g(x, f(x)) for x in series if f(x)]
要计算终值你既需要x
和f(x)
没问题,把它们都像这样:
[g(x, y) for (x, y) in ( (x, f(x)) for x in series ) if y ]
还是那句话:这应该是使用发电机(圆括号),而不是一个列表理解(方括号)。 否则,你开始过滤结果之前,您将构建整个列表。 这是列表理解的版本:
[g(x, y) for (x, y) in [ (x, f(x)) for x in series ] if y ] # DO NOT USE THIS
Answer 7:
您可以使用记忆化 。 它是设置在用于避免通过保存某处对每个计算出的值的结果做同样的计算两次的技术。 我看到,已经有一个使用记忆化的答案,但我想提出一个通用的实现,使用python装饰:
def memoize(func):
def wrapper(*args):
if args in wrapper.d:
return wrapper.d[args]
ret_val = func(*args)
wrapper.d[args] = ret_val
return ret_val
wrapper.d = {}
return wrapper
@memoize
def f(x):
...
现在f
本身就是一memoized版本。 有了这个实现,你可以memoize的使用任何功能@memoize
装饰。
Answer 8:
已经有关于记忆一个很多答案。 Python的3标准库现在有一个lru_cache
,这是最后的最近使用的缓存 。 所以你可以:
from functools import lru_cache
@lru_cache()
def f(x):
# function body here
这样,你的功能才会被调用一次。 您也可以指定大小lru_cache
,默认情况下,这是128与上面显示的memoize的装饰问题是列表的大小失控生长良好。
Answer 9:
使用map()
comp = [x for x in map(f, l) if x]
f
是函数f(X)
l
是列表
map()
将返回的结果f(x)
为列表中的每个x。
Answer 10:
这里是我的解决方案:
filter(None, [f(x) for x in l])
Answer 11:
开始Python 3.8
,并引进了赋值表达式(PEP 572) ( :=
运算符),这是可能的,以避免调用两次相同的功能,使用列表理解中的一个局部变量:
在我们的情况下,我们可以命名的评价f(x)
作为变量y
在使用表达式的结果来过滤列表,而且作为映射值:
[y for x in l if (y := f(x))]
Answer 12:
如何定义:
def truths(L):
"""Return the elements of L that test true"""
return [x for x in L if x]
这样,例如
> [wife.children for wife in henry8.wives]
[[Mary1], [Elizabeth1], [Edward6], [], [], []]
> truths(wife.children for wife in henry8.wives)
[[Mary1], [Elizabeth1], [Edward6]]
文章来源: Python list comprehension - want to avoid repeated evaluation