编写简短高效的 Python 代码并不总是那么容易或直接。然而,我们经常看到一段代码,却没有意识到其编写方式背后的思考过程。我们将看一下片段,它返回两个迭代之间的差异,以了解其结构。
根据片段功能的描述,我们可以天真地写成这样:
- def difference(a, b):
- return [item for item in a if item not in b]
此实现可能运行良好,但不考虑b
. 如果第二个列表中有许多重复项,这会使代码花费更多的时间。为了解决这个问题,我们可以使用set()
只保留列表中唯一值的方法:
- def difference(a, b):
- return [item for item in a if item not in set(b)]
这个版本虽然看起来像是一个改进,但实际上可能比以前的版本慢。如果你仔细观察,你会发现set()
每次都会调用 everyitem
导致a
结果set(b)
被评估。这是一个示例,我们set()
用另一种方法包装以更好地展示问题:
- def difference(a, b):
- return [item for item in a if item not in make_set(b)]
-
- def make_set(itr):
- print('Making set...')
- return set(itr)
-
- print(difference([1, 2, 3], [1, 2, 4]))
- # Making set...
- # Making set...
- # Making set...
- # [3]
此问题的解决方案是set()
在列表推导之前调用一次并存储结果以加快处理速度:
- def difference(a, b):
- _b = set(b)
- return [item for item in a if item not in _b]
在性能方面值得一提的另一个选项是使用列表推导与filter()
and list()
。使用后一个选项实现相同的代码将导致如下所示:
- def difference(a, b):
- _b = set(b)
- return list(filter(lambda item: item not in _b, a))
使用timeit
分析最后两个代码示例的性能,很明显使用列表推导可以比替代方法快十倍。这是因为它是一种本地语言功能,其工作方式与简单循环非常相似,for
没有额外函数调用的开销。这解释了为什么我们更喜欢它,除了可读性。