迭代器提供了对现有数据结构进行迭代的有效方法。
生成器提供了动态生成序列元素的有效方法。
迭代器示例
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 内置函数。一旦可迭代的元素被确定为True,any 将返回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_thing 在 all 100 个索引处进行评估,而第一种仅调用 expensive_thing 直到找到 True 值 (i=11) .