20、迭代器与生成器
(1)、迭代器(iterator)
<1>可迭代对象(interable)
在一个给定的list或tuple中,我们可以通过for循环来遍历这个list或tuple,这种遍历叫做迭代(interation)。
for循环中,遍历的对象,也叫做可迭代对象。例如,list、tuple、dict、set、str等。
<2>可迭代对象与迭代器的对比
举例:
Li = [1,2,3,4]
y = iter(Li)
print(next(y))#1
print(next(y))#2
print(type(y))#<class 'list_iterator'> 列表生成器类型
说明:程序中,变量Li是一个列表,属于可迭代对象,可以实现iter方法(或者说,可调用iter()函数)。变量y则是是一个迭代器,可调用next()函数。
<3>迭代器
迭代器内部持有一个状态,该状态用于记录当前迭代所在的位置,以方便下次迭代的时候获取正确的元素。它能在你调用next()方法的时候返回容器中的下一个值,任何实现了next()(在Python2中实现next())的方法的对象都是迭代器。
通俗点说,满足两个条件就可以
.有iter方法
.有next方法
迭代器的创建:
Li = [1,2,3,4]
y = iter(Li)#y就变成了一个迭代器
print(next(y))#1
print(next(y))#2通过调用next()方法
print(type(y))#<class 'list_iterator'> 列表生成器类型
说明:每次调用next()方法的时候做两件事:
.为下一次调用next()方法修改状态
.为当前这次调用生成返回结果
迭代器就像是一个懒加载的工厂,等到有人需要的时候才给它生成返回值,没调用的时候就处于休眠状态等待下一次调用。大大的减少了内存的占用。
<4> for i in (itreable)的内部实现
在大多情况下,我们不会一次次调用next方法去取值,而是通过
for i in(itreable)
例如:
**注意:**in后面的对象如果是一个迭代器,内部因为有iter方法才可以进行操作,所以,迭代器协议里面有iter和next方法,否则for语句无法应用。
for循环内部三件事:
1、调用可迭代对象的iter方法,返回一个迭代器对象
2、不断调用迭代器对象的next()方法
3、处理stopIteration
(2)、生成器(generator)
<1>生成器其实是一种特殊迭代器。但迭代器不一定是生成器。关键字yield
用生成器来实现斐波那契数列的例子
'''
斐波那契数列:
n:0 1 2 3 4 5 6
f:0 1 1 2 3 5 8 13 21
B A
B A
B A
B A
'''
def fib(max):
n,before,after =0,0,1
while n < max:
yield after
# before, after = after,before + after #从右往左计算赋值
temp = before
before = after
after = temp + after
n += 1
for i in fib(5):
print(i)
说明: fib 就是一个普通的python函数,它特需的地方在于函数体中没有 return 关键字,函数的返回值是一个生成器对象。当执行 f=fib(5) 返回的是一个生成器对象,此时函数体中的代码并不会执行,只有显示或隐示地调用next的时候才会真正执行里面的代码。
yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator,在 for 循环执行时,每次循环都会执行 fib 函数内部的代码,执行到 yield after 时,fib 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
<2>Return
在一个生成器中,如果没有return,则默认执行到函数完毕;如果有return,并在在执行过程中遇到return,则直接跳出StopIteration 终止迭代。
def f():
yield 'ok'
print('wlecome')
return
yield 'haha'
print('xixi')
f = f()
print(next(f))
print(next(f))
#——————————————————————
for i in f:
print(i)
说明:执行到第二个print(next(f)),跳出StopIteration 终止迭代。
但是如果,使用for循环,遇到return并没有报错,是因为for内部处理了迭代结束的这种情况。
<3>Send
def bar():
print('ok1')
count = yield 1
print(count)
print('ok2')
yield 2
b = bar()#创建生成器对象
b.send(None)#next(b) 第一次send前如果没有next,只能传一个send(None)
c = b.send('hyq')
print(c)
说明:执行到这句b.send(None),等同于next(b),执行流程,打印‘ok1’,遇到yield, 中断执行函数体里内容。然后,跳到c = b.send(‘hyq’),接着从yield执行,并把‘hyq’赋值给count,继续执行接下来的代码,直到再次遇到yield,返回值为2,程序结束。