2

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

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

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

  • 执行一些相对 CPU 开销较大的操作,并且:
  • 用于yield将产品一一返回给调用者。

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

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

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

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

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

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

4

2 回答 2

2

列表推导将始终运行到最后并将所有结果保存在内存中。

如果使用得当,生成器(表达式与否)可以短路——例如if product_id in <some generator>,一旦找到匹配项,它确实会停止,只有在没有匹配项时才会运行到最后。

于 2015-02-24T05:45:40.153 回答
1

列表理解的主要目的是创建一个新列表。所以,它只会在它运行它的过程并准备一个新列表时停止。运算符将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检查后转换为列表时,会给出生成器表达式生成的其余元素。

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

于 2015-02-24T05:58:53.897 回答