迭代器和生成器总结
迭代器:
是一个抽象的概念,任何对象,如果它的类有 next 方法和 iter 方法返回自己本身,对于 string、list、dict、tuple 等这类容器对象,使用 for 循环遍历是很方便的。在后台 for 语句对容器对象调用 iter()函数,iter()是 python 的内置函数。iter()会返回一个定义了 next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是 python 的内置函数。在没有后续元素时,next()会抛出一个 StopIteration 异常。
关于iter()和next():
1、iter(iterable)函数是把可迭代对象的迭代器取出来,内部是调用可迭代对象的__iter__方法,来取得迭代器的
2、next(iterator)函数是通过迭代器取得下一个位置的值,内部是调用迭代器对象的__next__方法,来取得下一个位置的值
注意:
当我们已经迭代完最后一个数据之后,再次调用next()函数会抛出StopIteration的异常,来告诉我们所有数据都已迭代完成,不用再执行next()函数了。
所以:
我们要想构造一个迭代器,就要实现它的__next__方法。但这还不够,python要求迭代器本身也是可迭代的,所以我们还要为迭代器实现__iter__方法,而__iter__方法要返回一个迭代器,迭代器自身正是一个迭代器,所以迭代器的__iter__方法返回自身即可。
结论:
一个实现了__iter__方法和__next__方法的对象,就是迭代器,迭代器同时也是一个可迭代对象
示例代码:
class FibIterator(object): """斐波那契数列迭代器""" def __init__(self, n): # 记录生成fibonacci的数列的个数 self.n = n # 记录当前遍历的下标 self.current_index = 0 # 记录fibonacci数列前面的两个值 self.num1 = 0 self.num2 = 1 def __next__(self): """被next()函数调用来获取下一个数""" if self.current_index < self.n: num = self.num1 self.num1, self.num2 = self.num2, self.num1 + self.num2 self.current_index += 1 return num else: raise StopIteration def __iter__(self): """迭代器的__iter__返回自身即可""" return self if __name__ == '__main__': fib = FibIterator(10) for num in fib: print(" ", num, end="")
# 运行结果: 0 1 1 2 3 5 8 13 21 34
生成器(Generator):
是创建迭代器的简单而强大的工具。生成器是一种特殊的迭代器,它比迭代器写起来更加优雅,它们写起来就像是正规的函数,只是在需要返回数据的时候使用 yield 语句。每次 next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)
最简单的生成器就是把一个列表生成式的[ ]改成( )
例如:g = (x*2 for x in rang(5)),就是一个生成器
另外一种生成器的创建方法就是这一个函数里面加yield:
例如把上面的斐波那契函数改写成生成式如下:
def fib(n): current_index = 0 num1, num2 = 0, 1 while current_index < n: # print(num1) # 打印斐波那契数列 """ 1. 假如函数中有yield,则不再是函数,而是生成器 2. yield 会产生一个断点 3. 假如yield后面紧接着一个数据,就会把数据返回, 作为next()函数或者for ...in...迭代出的下一个值 """ yield num1 num1, num2 = num2, num1 + num2 current_index += 1 if __name__ == '__main__': # 假如函数中有yield,则不再是函数,而是一个生成器 gen = fib(10) # 生成器是一种特殊的迭代器 for num in gen: print(num)
# 运行结果: 0 1 1 2 3 5 8 13 21 34
通过代码可以很直观看出生成器更优雅更省代码
yield其实就是保存当前程序执行状态。你用 for 循环的时候,每次取一个元素的时候就会计算一次。用 yield 的函数叫 generator,和 iterator 一样,它的好处是不用一次计算所有元素,而是用一次算一次,可以节省很多空间。generator每次计算需要上一次计算结果,所以用 yield,否则一 return,上次计算结果就没了,简单来说一个函数里面加了yield就是生成器。
还有我们除了可以使用next()函数来唤醒生成器继续执行外,还可以使用send()函数来唤醒执行。使用send()函数的一个好处是可以在唤醒的同时向断点处传入一个附加数据。
总结:
1、使用了yield关键字的函数不再是函数,而是生成器。(使用了yield的函数就是生成器)
2、yield关键字有两点作用:
保存当前运行状态(断点),然后暂停执行,即将生成器(函数)挂起
将yield关键字后面表达式的值作为返回值返回,此时可以理解为起到了return的作用
3、可以使用next()函数让生成器从断点处继续执行,即唤醒生成器(函数)
区别:
生成器能做到迭代器能做的所有事,而且因为自动创建了 iter()和 next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出 StopIteration 异常。