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

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

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

当前回答

奇怪的是,不同的人的美相差如此之大。我发现列表理解比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

一般过滤器稍快,如果使用内置函数。

在您的情况下,我希望列表理解稍微快一些

我会得出结论:使用列表理解而不是过滤器,因为它

更具可读性 更多的神谕的 更快(对于Python 3.11,参见附带的基准测试,也参见)

请记住,filter返回一个迭代器,而不是一个列表。

python3 -m timeit '[x for x in range(10000000) if x % 2 == 0]'            

1个循环,5个最佳:每循环270毫秒

python3 -m timeit 'list(filter(lambda x: x % 2 == 0, range(10000000)))'

1个循环,最好的5:432毫秒每循环

我觉得第二种方法更容易读懂。它确切地告诉你目的是什么:过滤列表。 注意:不要使用list作为变量名

除了公认的答案之外,还有一种极端情况,即您应该使用过滤器而不是列表推导式。如果列表是不可哈希的,则不能使用列表推导式直接处理它。一个真实的例子是使用pyodbc从数据库读取结果。游标的fetchAll()结果是一个不可哈希的列表。在这种情况下,要直接对返回的结果进行操作,应该使用filter:

cursor.execute("SELECT * FROM TABLE1;")
data_from_db = cursor.fetchall()
processed_data = filter(lambda s: 'abc' in s.field1 or s.StartTime >= start_date_time, data_from_db) 

如果你在这里使用列表理解,你会得到错误:

TypeError:不可哈希类型:list