两个概念
迭代器:实现了__next__()方法的对象,用于对象元素的演进遍历
可迭代对象:实现了__iter__()方法的对象,用于生成该对象对应的迭代器
for循环
到目前为止,您可能已经注意到大多数容器对象都可以使用 for 语句:
for element in [1, 2, 3]:
print(element)
for element in (1, 2, 3):
print(element)
for key in {'one':1, 'two':2}:
print(key)
for char in "123":
print(char)
for line in open("myfile.txt"):
print(line, end='')
在这些表达式的背后,for语句会调用容器对象中的__iter__()方法。该方法返回一个实现了__next__()方法的迭代器,该方法将逐一访问容器中的元素。当元素用尽时,__next__()将引发StopIteration异常来通知for循环终止。
所以对于一个for循环,它其实可以等价于:
_iterable = iter(iterable) # iterable.__iter__()
while True:
try:
do_something(next(_iterable)) # _iterable.__next__()
except StopIteration:
break
生成器
生成器的外部行为和迭代器很相似,它的创建方法类似于标准的函数,但它的返回语句不是return而是yield,如下是生成器的构建模板
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
当利用这个函数模板生成一个实例对象后,这个实例对象自动创建__iter__()和__next__()方法。同时这个实例对象不像普通函数那样直接被执行,而是像迭代器那样在调用next时才执行,同时它也会像迭代器那样对自身的演进状态进行保存。,当迭代结束时,它也会自动引发StopIteration。
下面是其使用实例演示
>>> for char in reverse('golf'):
... print(char)
...
f
l
o
g
生成器相比于迭代器的好处是它会自动生成__iter__()和__next__()方法,写起来更简洁,省事很多。
生成器推导式
某些简单的生成器可以写成简洁的表达式代码,所用语法类似列表推导式,将外层为圆括号而非方括号。 这种表达式被设计用于生成器将立即被外层函数所使用的情况。 生成器表达式相比完整的生成器更紧凑但较不灵活,相比等效的列表推导式则更为节省内存。
>>> sum(i*i for i in range(10)) # sum of squares
285
>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec)) # dot product
260
总结
生成器与迭代器的外部特性相同,但是这种外部特性的内部实现方式是完全不同的。Python中的for循环与C中的for循环是完全不一样的,他们的实现机理与外部特性都是完全不同的。