我有一个列表理解,它调用一个可能返回
None
的函数。
>>> f = lambda x: x if x < 3 else None
>>> l = [f(x) for x in [1,2,3,4]]
[1, 2, None, None]
我希望有上述的列表理解,没有“无”条目。
什么是更有效的方法来执行以下操作,而不产生额外的开销,同时保留列表理解的效率?
>>> filter(None, [f(x) for x in [1,2,3,4]])
[1, 2]
在你的理解中添加一个
if
,例如:
l = [y for y in (f(x) for x in [1,2,3,4]) if y is not None]
通过将 Generator Expression 放置在列表理解中,您只需要计算该函数一次。此外,生成器表达式是一个生成器,因此不需要额外的中间存储。
从 Python 3.8 开始,您可以使用 赋值表达式
(:=)
(又名:命名表达式或海象运算符)来避免对 f()
进行多次求值,例如:
l = [y for x in [1,2,3,4] if (y := f(x)) is not None]
Stephen 已经提到过,但是将 OP 中的
l
更改为生成器表达式(下面的g
)允许您仅评估 f
一次,而无需在中间创建不必要的列表。
f = lambda x: x if x < 3 else None
l = [f(x) for x in [1,2,3,4]] # <--- list (inefficient)
g = (f(x) for x in [1,2,3,4]) # <--- generator (efficient)
output = [x for x in g if x is not None]
您也可以在生成器上使用
filter()
(以获得更接近OP想法的解决方案):
output = list(filter(None, g))
注意
filter(None, g)
会过滤掉所有假值,因此会过滤掉0;如果您不想这样做,那么上面的列表理解可能是更干净的解决方案。
自 Python 3.9 起,创建单例列表并对其进行循环已得到优化。因此,您可以创建一个嵌套循环,其中第二个循环位于单例列表上,而不是使用生成器表达式(可能会意外耗尽)或海象运算符(它将循环变量泄漏到外部作用域):
output = [y for x in [1,2,3,4] for y in [f(x)] if y is not None]
简单的 timeit 测试(在 Python 3.12 上)表明,这实际上比其他选项(包括使用 walrus 运算符的选项)稍快。