列表推导式与lambda + filter

1110 投票
18 回答
839288 浏览
提问于 2025-04-15 23:48

我有一个列表,我想根据列表中每个项目的某个属性来筛选这些项目。

在以下几种方法中,哪一种更好呢?是从可读性、性能还是其他原因来看?

xs = [x for x in xs if x.attribute == value]
xs = filter(lambda x: x.attribute == value, xs)

18 个回答

92

由于速度差异通常非常小,所以使用过滤器还是列表推导式主要看个人喜好。一般来说,我更倾向于使用列表推导式(这似乎也和这里大多数其他回答一致),但有一种情况我更喜欢用filter

一个非常常见的用法是从某个可迭代对象X中提取出符合条件P(x)的值:

[x for x in X if P(x)]

但有时候你可能想先对这些值应用某个函数:

[f(x) for x in X if P(f(x))]


举个具体的例子,考虑一下:

primes_cubed = [x*x*x for x in range(1000) if prime(x)]

我觉得这看起来比使用filter稍微好一些。但现在考虑一下:

prime_cubes = [x*x*x for x in range(1000) if prime(x*x*x)]

在这种情况下,我们想要根据计算后的值来进行filter。除了需要计算立方值两次的问题(想象一下如果计算更复杂的话),还有就是表达式写了两次,这违反了DRY的原则。在这种情况下,我更倾向于使用:

prime_cubes = filter(prime, [x*x*x for x in range(1000)])
292

在Python这个圈子里,这个问题有点像宗教信仰一样。虽然Guido曾考虑把mapfilterreduce从Python 3中移除,但因为大家反对的声音很大,最后只有reduce被从内置函数移到了functools.reduce里。

我个人觉得列表推导式更容易理解。比如这个表达式[i for i in list if i.attribute == value],它的意思很清楚,所有的操作都在表面上,而不是藏在filter函数里面。

关于这两种方法的性能差异,我觉得不用太担心,因为差别不大。只有在你的应用程序中真的出现了性能瓶颈时,才需要考虑优化,但这种情况不太可能发生。

另外,既然BDFL想把filter从语言中去掉,那这就意味着列表推导式肯定更符合Python的风格了;-)

715

有趣的是,不同的人对美的理解差别很大。我觉得列表推导式比 filterlambda 更清晰,但你可以选择你觉得更简单的方式。

使用 filter 时,有两个方面可能会让它变得慢。

第一个是函数调用的开销:一旦你使用了一个 Python 函数(无论是用 def 还是 lambda 创建的),那么 filter 可能会比列表推导式慢。虽然这种差别通常不大,你也不需要太担心性能,除非你测试过代码发现它真的很慢,但这个差别是存在的。

另一个可能的开销是 lambda 需要访问一个作用域内的变量(value)。这比访问本地变量要慢,而在 Python 2.x 中,列表推导式只访问本地变量。如果你使用的是 Python 3.x,列表推导式会在一个单独的函数中运行,所以它也会通过闭包访问 value,这样就不会有这个差别了。

另一个可以考虑的选项是使用生成器,而不是列表推导式:

def filterbyvalue(seq, value):
   for el in seq:
       if el.attribute==value: yield el

然后在你的主代码中(可读性非常重要的地方),你用一个有意义的函数名替代了列表推导式和 filter

撰写回答