Python 列表理解与lambda+;滤器

Python 列表理解与lambda+;滤器,python,list,functional-programming,filter,lambda,Python,List,Functional Programming,Filter,Lambda,我碰巧发现自己有一个基本的过滤需求:我有一个列表,我必须根据项目的属性进行过滤 我的代码如下所示: my_list = [x for x in my_list if x.attribute == value] 但后来我想,这样写不是更好吗 my_list = filter(lambda x: x.attribute == value, my_list) 它更具可读性,如果性能需要,可以取出lambda来获得一些东西 问题是:在使用第二种方法时是否有任何警告?性能有什么不同吗?我错过了蟒蛇的方

我碰巧发现自己有一个基本的过滤需求:我有一个列表,我必须根据项目的属性进行过滤

我的代码如下所示:

my_list = [x for x in my_list if x.attribute == value]
但后来我想,这样写不是更好吗

my_list = filter(lambda x: x.attribute == value, my_list)
它更具可读性,如果性能需要,可以取出lambda来获得一些东西


问题是:在使用第二种方法时是否有任何警告?性能有什么不同吗?我错过了蟒蛇的方式吗™ 如果使用内置函数,则通常
过滤器
会稍微快一点


我希望你的列表理解速度稍微快一点

我发现第二种方法更具可读性。它准确地告诉您目的是什么:过滤列表。

注:不要使用“list”作为变量名

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

奇怪的是,不同的人有多少不同的优点。我发现列表的理解比过滤器要清晰得多,但是使用你觉得更容易的

有两件事可能会减慢您使用
过滤器的速度

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

可能应用的另一个开销是lambda被强制访问作用域变量(
value
)。这比访问局部变量慢,在Python2.x中,列表理解只访问局部变量。如果您使用的是Python3.x,那么列表理解将在一个单独的函数中运行,因此它也将通过闭包访问
,这种差异将不适用

另一个选择是使用生成器而不是列表理解:

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

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

这在Python中是一个有点宗教色彩的问题。即使,也有足够的反冲,最终只有
reduce
从内置移动到了

就我个人而言,我觉得列表理解更容易阅读。从表达式
[i for i in list if i.attribute==value]
可以更清楚地看到发生了什么,因为所有的行为都在表面上,而不是在过滤器函数内部

我不会太担心这两种方法之间的性能差异,因为这是微不足道的。我真的只会优化这一点,如果它被证明是你的应用程序中的瓶颈,这是不可能的

此外,由于所需的
过滤器已从语言中消失,因此肯定会自动使列表理解更具Pythonic;-)

我的看法

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

一个重要的区别是,列表理解将返回一个
列表
,而过滤器返回一个
过滤器
,您不能像
列表
那样对其进行操作(即:调用
len
,它与
过滤器
的返回不起作用)

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


也就是说,如果有一种方法可以从
过滤器中得到
列表
,这有点像在.NET中执行
lst.Where(i=>i.something()).ToList()
,我很想知道它


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

因为任何速度差异都注定是微不足道的,所以使用过滤器还是列表理解归根结底是一个品味问题。一般来说,我倾向于使用理解(这似乎与这里的大多数其他答案一致),但有一种情况下,我更喜欢
过滤器

一个非常常见的用例是从谓词p(X)中提取某些iterable 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)]
在这种情况下,我们希望根据后期计算的值过滤
。除了两次计算立方体的问题(想象一个更昂贵的计算),还有两次编写表达式的问题,这违反了美学。在这种情况下,我倾向于使用

prime_cubes = filter(prime, [x*x*x for x in range(1000)])
就是这样。它过滤掉列表中的元素。您可以看到定义中提到了相同的内容(在我前面提到的官方文档链接中)。然而,列表理解是在对上一个列表上的内容进行操作后生成新列表的内容。(过滤器和列表理解都会创建新列表,而不会代替旧列表执行操作。这里的新列表类似于具有全新数据类型的列表。例如,将整数转换为字符串等)

在您的示例中,根据定义,使用过滤器比使用列表理解更好。但是,如果您希望,比如说列表元素中的其他_属性,在您的示例中是作为新列表检索的,那么您可以使用列表理解

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

这就是我对过滤器和列表理解的实际记忆。删除列表中的一些内容并保持其他元素的完整性,使用过滤器。在元素中使用一些逻辑,创建一个适用于某些目的的淡化列表,使用列表理解。

当我需要在列表压缩后过滤某些内容时,我会使用一个简短的片段
return [item.other_attribute for item in my_list if item.attribute==value]
# 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])]
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)
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))
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
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) 
>>> 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
whitelist = set(range(0, 100000000, 27))

input_list = list(range(0, 100000000))

proximal_list = list(filter(
        lambda x: x in whitelist,
        input_list
    ))

proximal_list2 = [x for x in input_list if x in whitelist]

print(len(proximal_list))
print(len(proximal_list2))
import cProfile
from time import time


class BlockProfile:
    def __init__(self, profile_path):
        self.profile_path = profile_path
        self.profiler = None
        self.start_time = None

    def __enter__(self):
        self.profiler = cProfile.Profile()
        self.start_time = time()
        self.profiler.enable()

    def __exit__(self, *args):
        self.profiler.disable()
        exec_time = int((time() - self.start_time) * 1000)
        self.profiler.dump_stats(self.profile_path)


whitelist = set(range(0, 100000000, 27))
input_list = list(range(0, 100000000))

with BlockProfile("/path/to/create/profile/in/profile.pstat"):
    proximal_list = list(filter(
            lambda x: x in whitelist,
            input_list
        ))

    proximal_list2 = [x for x in input_list if x in whitelist]

print(len(proximal_list))
print(len(proximal_list2))