列表过滤:列表理解与lambda +filter

我碰巧发现自己有一个基本的过滤需求:我有一个列表,我必须通过项目的属性来过滤它。

我的代码看起来像这样:

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)?

奇怪的是,不同的人有多less美女变化。 我发现列表的理解比filter + lambda更清晰,但使用哪一个更容易。 但是,停止提供已经用于内置的variables名称,这是令人困惑的。

有两件事可能会减慢你的filter的使用。

第一个是函数调用的开销:只要你使用Python函数(无论是由def还是lambda创build的),filter可能比列表理解更慢。 这几乎肯定不够重要,除非你已经计算了代码,并且发现它是一个瓶颈,否则你不应该考虑性能问题。

另一个可能适用的开销是lambda被强制访问一个作用域variables( value )。 这比访问本地variables慢,在Python 2.x中,列表理解只能访问局部variables。 如果您使用的是Python 3.x,则列表理解将在单独的函数中运行,因此它也将通过闭包访问value ,并且此差异不适用。

另一种select是使用生成器而不是列表理解:

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

然后在你的主代码中(这是可读性真正重要的地方),你已经用一个有希望的函数名replace了列表理解和filter。

这在Python中是一个有点宗教的问题。 尽pipeGuido考虑从Python 3中移除mapfilterreduce ,但是还是有足够的反弹,最终只有reduce从内build移到了functools.reduce 。

我个人觉得列表理解更容易阅读。 从expression式[i for i in list if i.attribute == value]发生了什么,因为所有的行为都在表面上,而不是在filter函数内部。

我不会太担心两种方法之间的性能差异,因为它是边缘的。 如果它certificate是你的应用程序中的瓶颈,那么我真的只会优化它,这是不可能的。

此外,由于BDFL希望filter从语言中消失,那么肯定会自动使列表parsing更Pythonic 😉

由于任何速度差异都是微乎其微的,是否使用filter或列表parsing归结为味道的问题。 总的来说,我倾向于使用理解(这似乎与其他大多数答案一致),但是有一种情况我更喜欢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后计算的值。 除了计算立方体两次(想象一个更昂贵的计算)的问题之外,还有两次写入expression式的问题,违反了DRY美学。 在这种情况下,我会很好用

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

尽pipefilter可能是“更快的方式”,但“Pythonic方式”不会关心这些事情,除非性能是绝对关键的(在这种情况下,你不会使用Python!)。

一个重要的区别是,列表理解将返回一个list而filter返回一个filter ,你不能像list一样操作(即:调用len ,它不能与filter的返回一起工作)。

我自己的自学让我遇到了一些类似的问题。

也就是说,如果有一种方法可以从filter得到结果list ,有点像在.NET中做的事情,当你执行lst.Where(i => i.something()).ToList()好奇知道它。

编辑:这是Python 3,而不是2的情况(请参阅评论中的讨论)。

我发现第二种方式更可读。 它告诉你到底是什么意图:过滤列表。
PS:不要使用“list”作为variables名称

我想我只是在python 3中添加,filter()实际上是一个迭代器对象,所以你必须通过你的filter方法调用list(),以build立过滤列表。 所以在Python 2中:

 lst_a = range(25) #arbitrary list lst_b = [num for num in lst_a if num % 2 == 0] lst_c = filter(lambda num: num % 2 == 0, lst_a) 

列表b和c具有相同的值,并且与filter()是等效的[x for x in y if z]大致相同。 但是,在3中,这个相同的代码会使列表c包含一个filter对象,而不是一个过滤的列表。 为了在3中产生相同的值:

 lst_a = range(25) #arbitrary list lst_b = [num for num in lst_a if num % 2 == 0] lst_c = list(filter(lambda num: num %2 == 0, lst_a)) 

问题是list()需要一个可迭代的参数,并从该参数创build一个新的列表。 结果是在python 3中用这种方式使用filter需要的时间长达[x for x in y if z]方法的两倍,因为您必须遍历filter()以及原始列表的输出。

filter就是这样。 它过滤出列表的元素。 你可以看到定义提到相同(在我之前提到的官方文档链接)。 然而,列表理解是在对之前的列表进行处理之后产生一个新的列表(过滤和列表理解都会创build新的列表,而不是执行操作来代替旧的列表)。比如说一个全新的数据types,就像把整数转换成string一样)

在你的例子中,根据定义,使用filter比列表理解更好。 但是,如果你想要,从列表元素中说other_attribute,在你的例子中是作为一个新的列表检索,那么你可以使用列表理解。

 return [item.other_attribute for item in my_list if item.attribute==value] 

这是我真正记得有关filter和列表理解。 删除列表中的一些东西,保持其他元素不变,使用filter。 在元素上使用一些自己的逻辑,创build一个适合某种目的的淡化列表,使用列表理解。

通常如果使用内置函数, filter会稍微快一点。

我希望在你的情况下,列表的理解速度会稍微快一点

这是我在列表理解之后需要过滤某些东西时使用的一小段。 只是filter,拉姆达和列表的组合(也称为猫的忠诚和狗的清洁)。

在这种情况下,我正在阅读一个文件,删除空白行,注释掉行,以及在一行注释之后的任何内容:

 # Throw out blank lines and comments with open('file.txt', 'r') as lines: # From the inside out: # [s.partition('#')[0].strip() for s in lines]... Throws out comments # filter(lambda x: x!= '', [s.part... Filters out blank lines # y for y in filter... Converts filter object to list file_contents = [y for y in filter(lambda x: x != '', [s.partition('#')[0].strip() for s in lines])] 

lambda版本的一个优点是,如果条件依赖于它们,则可以捕获其他variables:

 value = 7 my_list = filter(lambda x, value=value: x.attribute == value, my_list) 

因为

 value = 7 my_list = [x for x in my_list if x.attribute == value] 

将返回name 'value' is not defined因为该variables值在条件中不可访问。

两个版本都可以在varaibale上检查。

我的

 def filter_list(list, key, value, limit=None): return [i for i in list if i[key] == value][:limit]