我有一个列表,我想通过项目的属性进行筛选。
以下哪个是首选(可读性,性能,其他原因)?
xs = [x for x in xs if x.attribute == value]
xs = filter(lambda x: x.attribute == value, xs)
我有一个列表,我想通过项目的属性进行筛选。
以下哪个是首选(可读性,性能,其他原因)?
xs = [x for x in xs if x.attribute == value]
xs = filter(lambda x: x.attribute == value, xs)
当前回答
除了公认的答案之外,还有一种极端情况,即您应该使用过滤器而不是列表推导式。如果列表是不可哈希的,则不能使用列表推导式直接处理它。一个真实的例子是使用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
其他回答
奇怪的是,不同的人的美相差如此之大。我发现列表理解比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作为一种编程语言是多么的灵活。人们可以使用任何逻辑,根据自己的才能和理解来编写程序。只要我们得到答案就好。
在您的情况下,这只是一个简单的过滤方法,可以由两者完成,但我更喜欢第一个my_list = [x for x in my_list if x.attribute == value],因为它看起来简单,不需要任何特殊的语法。任何人都可以理解这个命令,并在需要时进行更改。 (虽然第二种方法也很简单,但对于初级程序员来说,它仍然比第一种方法更复杂)
我会得出结论:使用列表理解而不是过滤器,因为它
更具可读性 更多的神谕的 更快(对于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毫秒每循环
总结其他答案
通过查看答案,我们已经看到了大量的反反复复,是否列表理解或过滤可能更快,或者关心这样的问题是否重要或python。最后,答案和大多数时候一样:视情况而定。
我只是在优化代码时偶然发现了这个问题,这个问题(尽管与in表达式结合在一起,而不是==)非常相关- filter + lambda表达式占用了我三分之一的计算时间(几分钟)。
我的情况
在我的例子中,列表理解要快得多(速度的两倍)。但我怀疑,根据过滤器表达式以及使用的Python解释器,这有很大的不同。
自己测试一下
下面是一个简单的代码片段,应该很容易适应。如果你对它进行剖析(大多数ide都可以很容易地做到这一点),你就可以很容易地为你的特定情况决定哪个是更好的选择:
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))
如果您没有一个IDE可以让您轻松地进行概要分析,那么可以试试这个(从我的代码库中提取,因此稍微复杂一点)。这段代码片段将为您创建一个配置文件,您可以轻松地使用例如snakeviz可视化:
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))