我有一个列表,我想通过项目的属性进行筛选。

以下哪个是首选(可读性,性能,其他原因)?

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

当前回答

由于任何速度差异都必然是微乎其微的,因此使用过滤器还是列表推导式都取决于个人喜好。一般来说,我倾向于使用推导式(这似乎与这里的大多数其他答案一致),但有一种情况下,我更喜欢过滤器。

一个非常常见的用例是根据谓词P(X)提取某个可迭代对象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)]

我认为这看起来比使用滤镜要好一点。但是现在想想

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

在本例中,我们希望根据后计算值进行过滤。除了计算立方体两次的问题(想象一个更昂贵的计算),还有编写表达式两次的问题,这违反了DRY美学。在这种情况下,我会使用

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

其他回答

奇怪的是,不同的人的美相差如此之大。我发现列表理解比filter+lambda清晰得多,但使用您认为更容易的。

有两件事可能会减慢你使用滤镜的速度。

首先是函数调用开销:一旦使用Python函数(无论是由def还是lambda创建的),过滤器很可能会比列表理解慢。几乎可以肯定,这并不重要,在对代码进行计时并发现它是一个瓶颈之前,您不应该过多地考虑性能,但区别是存在的。

可能应用的另一个开销是lambda被强制访问一个有作用域的变量(值)。这比在python2中访问局部变量要慢。X,列表推导式只访问局部变量。如果你使用的是Python 3。X,列表综合在一个单独的函数中运行,因此它也将通过闭包访问值,这种差异将不适用。

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

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

然后在你的主代码中(这是可读性真正重要的地方),你用一个有意义的函数名替换了列表理解和过滤器。

奇怪的是,在Python 3上,我看到过滤器执行得比列表推导更快。

我一直认为列表推导式的性能会更好。 喜欢的东西: [name为brand_names_db中的name,如果name不是None] 生成的字节码稍好一些。

>>> def f1(seq):
...     return list(filter(None, seq))
>>> def f2(seq):
...     return [i for i in seq if i is not None]
>>> disassemble(f1.__code__)
2         0 LOAD_GLOBAL              0 (list)
          2 LOAD_GLOBAL              1 (filter)
          4 LOAD_CONST               0 (None)
          6 LOAD_FAST                0 (seq)
          8 CALL_FUNCTION            2
         10 CALL_FUNCTION            1
         12 RETURN_VALUE
>>> disassemble(f2.__code__)
2           0 LOAD_CONST               1 (<code object <listcomp> at 0x10cfcaa50, file "<stdin>", line 2>)
          2 LOAD_CONST               2 ('f2.<locals>.<listcomp>')
          4 MAKE_FUNCTION            0
          6 LOAD_FAST                0 (seq)
          8 GET_ITER
         10 CALL_FUNCTION            1
         12 RETURN_VALUE

但它们实际上更慢:

   >>> timeit(stmt="f1(range(1000))", setup="from __main__ import f1,f2")
   21.177661532000116
   >>> timeit(stmt="f2(range(1000))", setup="from __main__ import f1,f2")
   42.233950221000214

我花了一些时间来熟悉高阶函数过滤器和映射。所以我习惯了他们,我实际上喜欢过滤器,因为它是明确的,它通过保持任何真实的过滤,我觉得很酷,我知道一些函数编程术语。

然后我读了这篇文章(Fluent Python Book):

映射和筛选函数仍然是内置的 在Python 3中,但是自从引入了列表推导式和generator ex‐ 压力没有那么重要。listcomp或genexp完成map和的工作 过滤器组合,但更可读。

现在我想,如果您可以使用已经广泛传播的习语(如列表推导)来实现它,那么为什么还要使用过滤器/映射的概念呢?此外,映射和过滤器是一种函数。在这种情况下,我更喜欢使用匿名函数lambdas。

最后,为了进行测试,我对两种方法(map和listComp)都进行了计时,我没有看到任何相关的速度差异,可以证明这是合理的。

from timeit import Timer

timeMap = Timer(lambda: list(map(lambda x: x*x, range(10**7))))
print(timeMap.timeit(number=100))

timeListComp = Timer(lambda:[(lambda x: x*x) for x in range(10**7)])
print(timeListComp.timeit(number=100))

#Map:                 166.95695265199174
#List Comprehension   177.97208347299602

我想我只是在python 3中添加,filter()实际上是一个迭代器对象,所以你必须将你的filter方法调用传递给list(),以构建过滤后的列表。所以在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包含一个筛选器对象,而不是一个筛选过的列表。要在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()接受一个可迭代对象作为参数,并从该参数创建一个新列表。结果是,在python 3中以这种方式使用filter所花费的时间是[x for x in y if z]方法的两倍,因为你必须遍历filter()的输出以及原始列表。

当我需要在列表理解之后过滤一些东西时,我使用了一小段。只是过滤器、lambda和列表的组合(或者称为猫的忠诚度和狗的清洁度)。

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

# 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])]