在重构一段代码时,我注意到了这一点:
if product_id in [c["id"] for c in self.data.load_products()]:
# Do something
反过来,load_products()
执行SQL查询,并为每个产品执行:
yield
将产品逐个返回给来电者。据我所知,列表理解和生成器表达式之间的区别在于,在列表理解的情况下,所有产品都将从数据库加载并处理,即使第一个产品是匹配的。
因此,如果我用这样的生成器表达式替换它:
# ↴ ↴
if product_id in (c["id"] for c in self.data.load_products()):
# Do something
它可以通过最终减少工作来改进代码,即一旦找到匹配,下一个产品将不会从数据库加载,也不会被处理。
尽管如此,我还是不太了解Python。
我是对的吗? Python会在找到匹配项后立即停止,或者两段代码都会执行相同操作并从数据库加载每个产品吗?
答案 0 :(得分:2)
列表理解将始终运行到最后并将所有结果保存在内存中。
生成器(表达与否)可以短路(如果使用得当) - 例如if product_id in <some generator>
确实会在找到匹配后立即停止,只有在那里运行到最后不配。
答案 1 :(得分:1)
List Comprehension的主要目的是创建一个新列表。因此,只有当它运行并准备一个新列表时才会停止。如果列表中的任何项与正在搜索的实际项匹配,in
运算符将迭代新生成的列表并返回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
对象会给出生成器表达式生成的其余元素。
因此,在这种情况下,Generator Expressions更好。