我有一个列表,我想通过项目的属性进行筛选。
以下哪个是首选(可读性,性能,其他原因)?
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)
当前回答
总结其他答案
通过查看答案,我们已经看到了大量的反反复复,是否列表理解或过滤可能更快,或者关心这样的问题是否重要或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))
其他回答
一个重要的区别是,列表推导式将返回一个列表,而过滤器返回一个过滤器,你不能像操作列表一样操作它(即:在它上调用len,它不能与过滤器的返回一起工作)。
我自己的自学也让我遇到了类似的问题。
也就是说,如果有一种方法可以从过滤器获得结果列表,有点像在。net中执行lst时所做的那样。Where(i => i.something()). tolist(),我很想知道它。
编辑:这是Python 3的情况,而不是Python 2(见评论中的讨论)。
总结其他答案
通过查看答案,我们已经看到了大量的反反复复,是否列表理解或过滤可能更快,或者关心这样的问题是否重要或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))
我会得出结论:使用列表理解而不是过滤器,因为它
更具可读性 更多的神谕的 更快(对于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 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
过滤器就是这样。它过滤掉列表中的元素。你可以看到定义中提到了同样的内容(在我之前提到的官方文档链接中)。然而,列表理解是在对前一个列表上的内容进行操作后产生一个新的列表。(过滤器和列表推导式都创建新列表,而不执行替换旧列表的操作。这里的新列表类似于具有全新数据类型的列表。比如将整数转换为字符串,等等)
在您的示例中,根据定义,使用过滤器比使用列表理解更好。但是,如果您希望,例如列表元素中的other_attribute,在您的示例中是作为一个新列表检索,那么您可以使用列表推导式。
return [item.other_attribute for item in my_list if item.attribute==value]
这就是我对筛选器和列表理解的记忆。删除列表中的一些东西,并保持其他元素完整,使用过滤器。在元素上使用一些自己的逻辑,并创建一个适合某些目的的稀释列表,使用列表理解。