出处: 菜鸟教程 - Python3 迭代器与生成器
迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:iter()
和 next()
。
字符串,列表或元组对象都可用于创建迭代器:
>>> list = [1, 2, 3, 4]
>>> it = iter(list) # 创建迭代器对象
>>> print (next(it)) # 输出迭代器的下一个元素
1
>>> print (next(it))
2
把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()
与 __next__()
。
__iter__()
方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next__()
方法并通过 StopIteration 异常标识迭代的完成。
__next__()
方法会返回下一个迭代器对象。
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter)) # 1
print(next(myiter)) # 2
print(type(myclass)) #
print(type(myiter)) #
print(myclass is myiter) # True
print(next(myclass)) # 1
print(next(myclass)) # 2
StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 next() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
在 Python 中,使用了 yield
的函数被称为生成器(generator)。
yield
是一个关键字,用于定义生成器函数,生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
def countdown(n):
while n > 0:
yield n
n -= 1
# 创建生成器对象
generator = countdown(5)
# 通过迭代生成器获取值
print(next(generator)) # 输出: 5
print(next(generator)) # 输出: 4
print(next(generator)) # 输出: 3
# 使用 for 循环迭代生成器
for value in generator:
print(value) # 输出: 2 1
出处: 菜鸟教程 - Python yield 使用浅析
在 python 2 中,range() 为 list 对象,会生成 list,xrange() 为 iterable 对象。在 python 3 中,range() 和 xrange() 合并了。
可以利用 isgeneratorfunction
判断一个函数是否是一个特殊的 generator 函数。
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b # 使用 yield
a, b = b, a + b
n = n + 1
from inspect import isgeneratorfunction
print(isgeneratorfunction(fab)) # True
fab 是一个 generator function,而 fab(5) 是调用 fab 返回的一个 generator,好比类的定义和类的实例的区别
import types
print(isinstance(fab, types.GeneratorType)) # False
print(isinstance(fab(5), types.GeneratorType)) # True
fab 是无法迭代的,而 fab(5) 是可迭代的
from collections import Iterable
print(isinstance(fab, Iterable)) # False
print(isinstance(fab(5), Iterable)) # True
在一个 generator function 中,如果没有 return,则默认执行至函数完毕,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。
另一个 yield
的例子来源于文件读取。如果直接对文件对象调用 read()
方法,会导致不可预测的内存占用。好的方法是利用固定长度的缓冲区来不断读取文件内容。通过 yield
,我们不再需要编写读文件的迭代类,就可以轻松实现文件读取。
def read_file(fpath):
BLOCK_SIZE = 1024
with open(fpath, 'rb') as f:
while True:
block = f.read(BLOCK_SIZE)
if block:
yield block
else:
return