我有一个列表,想要根据项的属性进行过滤。
以下哪个更好(可读性、性能、其他原因)?
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)
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
就性能而言,这要看情况。
filter
不会返回列表,而是一个迭代器。如果你需要“立即”过滤并转换成列表,对于非常大的列表(>1M),使用列表推导比使用 filter 和列表转换慢约40%。对于100K个元素以下的列表,几乎没有区别;从600K开始就会出现差异。
如果不转换为列表,则 filter
几乎是瞬间完成的。
更多信息请见:https://blog.finxter.com/python-lists-filter-vs-list-comprehension-which-is-faster/
pyodbc
从数据库读取结果,则 cursor
的 fetchAll()
结果是一个不可哈希的列表。在这种情况下,应该使用过滤器来直接操作返回的结果: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)
>>> hash(list()) # TypeError: unhashable type: 'list'
其次,这个可以正常工作:processed_data = [s for s in data_from_db if 'abc' in s.field1 or s.StartTime >= start_date_time]
- Thomas Graingerpython3 -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 for name in brand_names_db if name is not 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
filter
版本中,您没有传递lambda函数,这会使其默认为恒等函数。在列表推导中定义if not None
时,您正在定义一个lambda函数(请注意MAKE_FUNCTION
语句)。其次,两个版本的结果是不同的,因为列表推导版本只会删除None
值,而filter
版本将删除所有“假值”。话虽如此,微基准测试的整个目的是无意义的。这些是一百万个迭代,每个迭代有1k个项目!差异是微不足道的。 - Victor Schröderlist(filter(None, seq))
等价于 [i for i in seq if i]
,而不是 i 不为 None
。https://docs.python.org/3/library/functions.html#filter - Sole Sensei浏览了其他答案后,我们看到了很多争论,无论是列表推导式还是过滤器可能更快,或者是否重要或符合Pythonic关心这样的问题。最终,答案像大多数时候一样:取决于情况。
我在优化代码时偶然遇到了这个问题(虽然与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作为一种编程语言的灵活性,一个人可以使用任何逻辑并根据他们的才能和理解编写程序。只要我们得到答案就可以。
在你的情况下,这只是一个简单的过滤方法,两种方法都可以实现,但我更喜欢第一种my_list = [x for x in my_list if x.attribute == value]
,因为它看起来简单,不需要特殊语法。任何人都可以理解这个命令,并进行必要的更改。
(虽然第二种方法也很简单,但对于初学者级别的程序员来说,它仍然比第一种方法更复杂)
filter
更易读。当你有一个简单的表达式可以直接在列表推导式中使用,但必须将其包装在 lambda 函数(或类似地由partial
或operator
函数构建等)中才能传递给filter
时,此时列表推导式胜出。 - abarnertfilter
函数的返回值是一个过滤器生成器对象,而不是一个列表。 - Matteo Ferlafilter
的具体实现并不是很易读(这并不奇怪,因为 Python 并不是真正的函数式编程语言)。在语言开发历史中,交换参数顺序可能是更好的选择,即filter(xs, lambda: x: ...)
将从左到右阅读,就像“过滤 xs 以仅保留满足条件的值”一样。可以说,推导式应该被认为更易读,因为它是从左到右可理解的(你看我做了什么吗?),并且基于 Python 的非 FP 语言属性和filter
的不太易读的实现等方面更符合“Pythonic”的风格。 - Ezekiel Victor