Ste*_*fan 54 python list-comprehension code-readability
我有一个列表理解,近似于:
[f(x) for x in l if f(x)]
Run Code Online (Sandbox Code Playgroud)
其中l是列表,f(x)是返回列表的昂贵函数.
我想避免对f(x)的每次非空出现进行两次f(x)求值.有没有办法在列表理解中保存其输出?
我可以删除最终条件,生成整个列表然后修剪它,但这似乎是浪费.
Inb*_*ose 11
你应该使用memoize装饰器.这是一个有趣的链接.
使用链接中的memoization和"代码":
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)]
Run Code Online (Sandbox Code Playgroud)
Enr*_*eri 11
一个解决方案(如果你有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)]
Run Code Online (Sandbox Code Playgroud)
然后在列表解析中使用此函数.这种方法在两种条件下有效,一种是理论上的,一种是实用的.第一个是函数f应该是确定性的,即在给定相同输入的情况下返回相同的结果,另一个是对象x可以用作字典键.如果第一个无效,则应每次按定义重新计算f,而如果第二个失败,则可以使用一些稍微更强大的方法.
你可以在网上找到大量的memoization实现,我认为python的新版本也包含了一些内容.
从侧面说明,永远不要使用小L作为变量名,这是一个坏习惯,因为它可能与某些终端上的i或1混淆.
编辑:
如评论所示,使用生成器理解(以避免创建无用的重复临时值)的可能解决方案将是这样的表达式:
[g(x, fx) for x, fx in ((x,f(x)) for x in l) if fx]
Run Code Online (Sandbox Code Playgroud)
考虑到f的计算成本,原始列表中的重复次数以及处理时的内存,您需要权衡您的选择.Memoization进行空间速度权衡,这意味着它可以保存每个结果的记录,因此如果你有大量的列表,它可能会占用内存占用空间.
[y for y in [f(x) for x in l] if y]
Run Code Online (Sandbox Code Playgroud)
对于您更新的问题,这可能很有用:
[g(x,y) for x in l for y in [f(x)] if y]
Run Code Online (Sandbox Code Playgroud)
不.没有(干净的)方法来做到这一点.一个老式的循环没有错:
output = []
for x in l:
result = f(x)
if result:
output.append(result)
Run Code Online (Sandbox Code Playgroud)
如果您发现难以阅读,您可以始终将其包装在一个函数中.
如前面的答案所示,您可以使用双重理解或使用记忆.对于合理大小的问题,这是一个品味问题(并且我同意memoization看起来更干净,因为它隐藏了优化).但是如果你正在检查一个非常大的列表,那就有很大的不同: Memoization会存储你计算的每一个值,并且可以快速消耗你的记忆.使用发电机(圆形的parens,而不是方括号)的双重理解只存储您想要保留的内容.
来解决你的实际问题:
[g(x, f(x)) for x in series if f(x)]
Run Code Online (Sandbox Code Playgroud)
要计算最终值,您需要x
和f(x)
.没问题,把它们都传递给他们:
[g(x, y) for (x, y) in ( (x, f(x)) for x in series ) if y ]
Run Code Online (Sandbox Code Playgroud)
再次:这应该使用生成器(圆形的parens),而不是列表理解(方括号).否则,在开始过滤结果之前,您将构建整个列表.这是列表理解版本:
[g(x, y) for (x, y) in [ (x, f(x)) for x in series ] if y ] # DO NOT USE THIS
Run Code Online (Sandbox Code Playgroud)
开始Python 3.8
,并引入赋值表达式(PEP 572)(:=
运算符),可以在列表推导式中使用局部变量以避免两次调用相同的函数:
在我们的例子中,我们可以将 的评估命名f(x)
为变量,y
同时使用表达式的结果来过滤列表以及映射值:
[y for x in l if (y := f(x))]
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6707 次 |
最近记录: |