我读了一些关于迭代器和生成器之间差异的问题和answers。但我不明白你何时应该选择其中一个?当一个比另一个更好的时候,你知道任何例子(简单的,现实的)吗?谢谢。
答案 0 :(得分:4)
迭代器提供了迭代现有数据结构的有效方法。
生成器提供了动态生成序列元素的有效方法。
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
)每次都要求读取一行。
生成器动态生成序列的元素。请考虑以下事项:
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
内置函数。只要可迭代元素被确定为any
,True
就会返回True
。因此,当您将生成器函数传递给any
时,它只会根据需要多次调用expensive_thing(i)
来查找True
- ish值。
将此与使用传递给any
的列表推导进行比较:
first = any([expensive_thing(i) for i in range(100)])
在这种情况下,expensive_thing(i)
将调用{em>所有值i
,第一个,然后是100个元素列表True
1}} / False
值将提供给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_thing
在所有 100个索引处进行评估,而第一个只调用expensive_thing
,直到找到True
值(i=11
)。