列表过滤:列表理解与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中移除map
, filter
和reduce
,但是还是有足够的反弹,最终只有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]