与列表理解相比,生成器表达式做的工作更少吗?

Are generator expressions doing less work compared to list comprehensions?

在重构一段代码时,我注意到了这一点:

if product_id in [c["id"] for c in self.data.load_products()]:
    # Do something

反过来,load_products() 执行 SQL 查询,并且对于每个产品:

据我所知,列表理解和生成器表达式之间的区别在于,在列表理解的情况下,所有产品都将从数据库加载并处理,即使第一个产品是匹配的。

因此,如果我用这样的生成器表达式替换它:

#                ↴                                          ↴
if product_id in (c["id"] for c in self.data.load_products()):
    # Do something

它可以通过最终减少工作量来改进代码,即一旦找到匹配项,就不会从数据库加载下一个产品,也不会对其进行处理。

不过,我不太了解 Python 可以肯定。

我说得对吗? Python 是否会在找到匹配项后立即停止,或者两段代码会执行相同的操作并从数据库中加载每个产品?

列表理解总是运行结束并将所有结果保存在内存中。

生成器(表达式或非表达式)可以短路如果使用得当——例如if product_id in <some generator>确实会在找到匹配项后立即停止,只有运行没有匹配就结束

List Comprehension 的主要目的是创建一个新的列表。因此,它只会在运行并准备新列表时停止。 in 运算符将遍历新生成的列表和 returns True 如果列表中的任何项目与正在搜索的实际项目匹配。例如,

>>> lc = [item * 2 for item in range(10)]
>>> lc
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> 4 in lc
True

在生成器表达式的情况下,in 运算符将调用生成器的 __iter__ 方法,并且当 __iter__ 调用返回的项与实际匹配时它会立即停止正在搜索的项目。你可以这样确认

>>> ge = (item * 2 for item in range(10))
>>> ge
<generator object <genexpr> at 0x7f498a85fd70>
>>> 4 in ge
True
>>> list(ge)
[6, 8, 10, 12, 14, 16, 18]

正如您在此处看到的,ge 只会迭代直到找到匹配项。 ge 对象在 4 in ge 检查后转换为列表时,给出生成器表达式生成的其余元素。

因此,生成器表达式在这种情况下更好。