【问题标题】:When do you use iterators and when generators? [closed]你什么时候使用迭代器,什么时候使用生成器? [关闭]
【发布时间】:2015-03-29 01:23:13
【问题描述】:

我阅读了一些关于迭代器和生成器之间差异的问题和answers。但我不明白你什么时候应该选择一个而不是另一个?你知道一个比另一个更好的例子(简单的,现实生活中的例子)吗?谢谢。

【问题讨论】:

  • 具体有什么看不懂的?
  • 我是 python 新手。大多数事情对我来说都是新的。简单的例子比写“Python 的生成器提供了一种实现迭代器协议的便捷方式”或其他技术 gorgon 更好。
  • 需要一些技术术语的知识,你知道的越多,你就能更好地找到问题的答案并提出问题以获得更多更好的答案。要回答您的问题,请查看 Vincent Driessen's 关于 interator vs generator 问题的帖子。
  • 正如我在将近五年前的回答中解释的那样(投票最多但仍未接受:-),所有生成器都是迭代器(反之亦然),所以你的问题绝对没有任何意义——比如问“我什么时候应该吃意大利面而不是意大利面”,而意大利面意大利面的一个例子(实例,特例),或者问“我应该买拉布拉多犬还是狗”(因为拉布拉多犬 一种狗),等等。
  • 简洁,genexp -> 生成器。可扩展性、灵活性 -> 迭代器。

标签: python python-3.x


【解决方案1】:

迭代器提供了对现有数据结构进行迭代的有效方法。

生成器提供了动态生成序列元素的有效方法。

迭代器示例

Python 的文件阅读器可以用作迭代器。那么您可能使用什么来处理文件的一行:

with open('file.txt', 'rb') as fh:
    lines = fh.readlines()  # this reads the entire file into lines, now
    for line in lines:
        process(line)       # something to be done on each line

您可以使用迭代器更有效地实现

with open('file.txt', 'rb') as fh:
    for line in fh:         # this will only read as much as needed, each time
        process(line)

优势在于,在第二个示例中,您没有将整个文件读入内存,然后遍历行列表。相反,阅读器(Python3 中的BufferedReader)每次都读取一行内容。

生成器示例

Generators generate 动态序列的元素。考虑以下几点:

def fib():
    idx  = 0
    vals = [0,1]
    while True:
        # If we need to compute a new value, do so on the fly
        if len(vals) <= idx: vals.append(vals[-1] + vals[-2])
        yield vals[idx]
        idx += 1

这是一个生成器的例子。在这种情况下,每次“调用”它都会在Fibonacci sequence 中生成下一个数字。

我将“调用”放在引号中,因为从生成器获取连续值的方法与传统函数不同。

我们有两种主要的方式从生成器中获取值:

迭代它

# Print the fibonacci sequence until some event occurs
for f in fib():
    print(f)
    if f > 100: break

这里我们使用in 语法来迭代生成器,并打印返回的值,直到我们得到一个大于 100 的值。

输出:

0 1 1 2 3 5 8 13 21 34 55 89 144

调用 next()

我们也可以在生成器上调用next(因为generators are iterators)并(生成并)以这种方式访问​​值:

f = fib()

print(next(f))  # 0
print(next(f))  # 1
print(next(f))  # 1
print(next(f))  # 2
print(next(f))  # 3

不过,还有一些更有说服力的生成器示例。这些通常以"generator expressions" 的形式出现,这是一个相关概念 (PEP-289)。

考虑以下内容:

first = any((expensive_thing(i) for i in range(100)))

在这里,我们正在创建一个生成器表达式:

(expensive_thing(i) for i in range(100))

并将其传递给any 内置函数。一旦可迭代的元素被确定为Trueany 将返回True。因此,当您将生成器函数传递给any 时,它只会根据需要多次调用expensive_thing(i) 以找到True-ish 值。

将此与使用传递给any 的列表推导进行比较:

first = any([expensive_thing(i) for i in range(100)])

在这种情况下,expensive_thing(i) 将为i所有 值调用,首先,然后是True/@987654349 的 100 元素列表@ 值将被赋予 any,如果它找到 True-ish 值,它将返回 True

但如果expensive_thing(0) 返回True,显然更好的方法就是评估、测试它,然后停在那里。生成器允许你这样做,而像列表推导这样的东西则不允许。


考虑以下示例,说明使用生成器表达式相对于列表推导的优势:

import time

def expensive_thing(n):
    time.sleep(0.1)
    return 10 < n < 20

# Find first True value, by using a generator expression
t0 = time.time()
print( any((expensive_thing(i) for i in range(100))) )
t1 = time.time()
td1 = t1-t0

# Find first True value, by using a list comprehension
t0 = time.time()
print( any([expensive_thing(i) for i in range(100)]) )
t1 = time.time()
td2 = t1-t0

print("TD 1:", td1)  # TD 1:  1.213068962097168
print("TD 2:", td2)  # TD 2: 10.000572204589844

函数expensive_thing 引入了人为延迟来说明两种方法之间的区别。第二种(列表理解)方法需要更长的时间,因为 expensive_thingall 100 个索引处进行评估,而第一种仅调用 expensive_thing 直到找到 True 值 (i=11) .

【讨论】:

  • Python 3.x range 和 Python 2.x xrange 不是生成器。它们是惰性序列。生成器的定义是产生迭代器的函数。见docs.python.org/3/glossary.html#term-generator
  • 为了理解这个概念,它们可以 100% 被认为是生成器,并且由于这些内置函数可能是 OP 熟悉的,所以我使用了它们——但我会用合适的生成器更新我的帖子。
猜你喜欢
  • 1970-01-01
  • 2016-01-11
  • 1970-01-01
  • 1970-01-01
  • 2010-09-06
  • 2010-09-19
  • 2012-04-23
相关资源
最近更新 更多