有效过滤掉调用函数的列表理解中的“无”项

问题描述 投票:0回答:2

我有一个列表理解,它调用一个可能返回

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]
python python-2.7 list list-comprehension
2个回答
75
投票

在你的理解中添加一个

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+

从 Python 3.8 开始,您可以使用 赋值表达式

(:=)
(又名:命名表达式或海象运算符)来避免对
f()
进行多次求值,例如:

l = [y for x in [1,2,3,4] if (y := f(x)) is not None]

0
投票

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

自 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 运算符的选项)稍快。

© www.soinside.com 2019 - 2024. All rights reserved.