我碰巧发现自己有一个基本的过滤需求:我有一个列表,我必须通过项目的属性过滤它。
我的代码看起来像这样:
my_list = [x for x in my_list if x.attribute == value]
但后来我想,这样写它会不会更好?
my_list = filter(lambda x: x.attribute == value, my_list)
它更具可读性,如果需要性能,可以取出 lambda 来获得一些东西。
问题是:使用第二种方式有什么警告吗?任何性能差异?我是否完全错过了 Pythonic Way™并且应该以另一种方式(例如使用 itemgetter 而不是 lambda)来完成它?
很奇怪,不同的人有多少美丽。我发现列表理解比filter
+ lambda
更清晰,但是使用你发现的更容易。但是,请停止提供已用于内置函数的变量名称,这令人困惑。 [ 最初使用的问题list
作为变量名称,但是为响应此答案而更新了。 ]
有两件事可能会减慢您对filter
的使用速度。
第一个是函数调用开销:只要你使用 Python 函数(无论是由def
还是lambda
创建),过滤器很可能比列表理解慢。几乎可以肯定这并不重要,在你对代码进行计时并发现它成为瓶颈之前,你不应该考虑性能,但差异就在那里。
可能适用的另一个开销是 lambda 被强制访问范围变量( value
)。这比访问局部变量要慢,而在 Python 2.x 中,列表推导只访问局部变量。如果您使用的是 Python 3.x,则列表推导在一个单独的函数中运行,因此它也将通过闭包访问value
,这种差异将不适用。
另一个要考虑的选择是使用生成器而不是列表推导:
def filterbyvalue(seq, value):
for el in seq:
if el.attribute==value: yield el
然后在你的主代码中(这是可读性真正重要的地方)你已经用一个有希望的有意义的函数名替换了列表理解和过滤器。
这在 Python 中是一个有些宗教问题。尽管Guido 考虑从 Python 3 中删除map
, filter
和reduce
,但是有足够的强烈反对,最终只有reduce
从内置函数转移到functools.reduce 。
我个人认为列表推导更容易阅读。 [i for i in list if i.attribute == value]
所有行为都在表面上而不是在过滤器函数内部,那么表达式[i for i in list if i.attribute == value]
更明确地发生了什么。
我不会太担心两种方法之间的性能差异,因为它是边缘的。如果它被证明是你的应用程序的瓶颈,我真的只会优化它,这是不太可能的。
此外,因为BDFL希望filter
从语言中消失,然后肯定会自动使列表理解更加 Pythonic ;-)
由于任何速度差异必然微乎其微,因此无论是使用过滤器还是列表理解,都归结为品味问题。一般来说,我倾向于使用理解(这似乎与大多数其他答案一致),但有一种情况我更喜欢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)])