生成器表达式与列表推导非常相似。主要区别在于它不会立即创建完整的结果集;它创建了一个生成器对象,然后可以对其进行迭代。
例如,请参见以下代码中的区别:
# 清单理解 [x**2 for x in range(10)] # 输出:[0、1、4、9、16、25、36、49、64、81]
# 生成器理解 (x**2 for x in xrange(10)) # Output: <generator object <genexpr> at 0x11b4b7c80>
这是两个非常不同的对象:
列表推导返回一个list对象,而生成器推导返回一个对象generator。
generator无法为对象建立索引,而是无法使用该next函数按顺序获取项目。
注意:我们使用xrange它是因为它也会创建一个生成器对象。如果我们使用范围,则会创建一个列表。此外,xrange仅在更高版本的python 2中存在。在python 3中,range仅返回生成器。有关更多信息,请参见range和xrange函数之间的差异示例。
g = (x**2 for x in xrange(10)) print(g[0])
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'generator' object has no attribute '__getitem__'
g.next() # 0 g.next() # 1 g.next() # 4 ... g.next() # 81 g.next() # 引发StopIteration异常
Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
注:该功能应该被取代,并用自和Python 3中并不存在。g.next()next(g)xrangerangeIterator.next()xrange()
尽管这两种方法都可以通过类似的方式进行迭代:
for i in [x**2 for x in range(10)]: print(i) """ Out: 0 1 4 ... 81 """
for i in (x**2 for x in xrange(10)): print(i) """ Out: 0 1 4 . . . 81 """
生成器表达式是延迟计算的,这意味着它们仅在迭代生成器时才生成并返回每个值。在遍历大型数据集时,这通常很有用,避免了在内存中创建数据集副本的需要:
for square in (x**2 for x in range(1000000)): #做点什么
另一个常见用例是避免不必要地遍历整个可迭代对象。在此示例中,每次的迭代都从远程API检索项目get_objects()。可能存在成千上万个对象,必须逐一检索它们,而我们只需要知道是否存在与模式匹配的对象即可。通过使用生成器表达式,当我们遇到与模式匹配的对象时。
def get_objects(): """Gets objects from an API one by one""" while True: yield get_next_item() def object_matches_pattern(obj): # 执行潜在的复杂计算 return matches_pattern def right_item_exists(): items = (object_matched_pattern(each) for each in get_objects()) for item in items: if item.is_the_right_one: return True return False