迭代器
含有 iter() 方法或 getitem() 方法的对象称之为可迭代对象
。
迭代器协议
是指对象要具备的 iter() 和__next__() 方法,其中,iter() 方法返回迭代器对象本身,next() 方法返回容器的下一个元素,在没有后续元素时抛出 StopIteration 异常。
符合此协议的对象可称之为迭代器
。
for循环的实质是先通过可迭代对象的内置函数 iter() 获得一个迭代器,然后再不断调用迭代器的 next() 函数实现获取容器的元素。
for x in [1, 2, 3]:
print i
#=====等价于=====
# 获得 Iterator 对象
it = iter([1, 2, 3])
# 循环
while True:
try:
# 获得下一个值
x = next(it)
print x
except StopIteration:
# 没有后续元素,退出循环
break
创建一个迭代器
class MyNumbers:
def __init__(self):
self.a,self.b=0,1
def __iter__(self):
return self
def __next__(self):
self.a,self.b=self.b,self.a+self.b
return self.a
myclass = MyNumbers()
for i in myclass:
if i <10:
print(i)
else:
break
生成器
生成器也遵循迭代器协议,所以它是迭代器的一种。
生成器的构建方式有两种:
- 生成器函数:常规函数定义,但使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。
- 生成器表达式:类似于列表推导,但生成器返回按需产生结果的一个对象,而不是一次性构建一个结果列表。
生成器表达式和生成器函数都是产生一个生成器对象,生成器对象每次取一个对象元素,并不会一次性全部输出。
因为生成器函数,是一个函数,有更强的构造性,所以基本使用生成器时,还是以生成器函数为主。
创建一个简单的生成器
def generator1():
print ('hello 1')
yield 1
print ('hello 2')
yield 2
print('hello 3')
g = generator1() # 函数没有执行,而是返回了一个生成器,当然也是一个迭代器
a=next(g) # 当使用 next(g)时,函数体开始执行,遇到 yield 暂停,yield返回1给a
print(a)
b=next(g) #从原来暂停的地方继续执行
print(b)
next(g) # 从原来暂停的地方继续执行,没有遇到yield,抛出异常StopIteration
##输出结果如下:
#hello 1
#1
#hello 2
#2
#hello 3
#Traceback (most recent call last):
#StopIteration
先创建一个生成器函数,生成器函数的使用过程看起来就是不断地 执行->中断->执行->中断 的过程。next使得生成器接着上次中断的地方继续执行,生成器执行到yield则函数中断并返回一个值
通过for循环演示一个生成器
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
f=fib(6)##fib函数中使用了yield,那么fib()函数便成为一个生成器,为max=6时的生成器命名为f。
print(f)##打印生成器f的内存单元
for i in f:
print(i)
##通过循环生成器,将生成器中的数据取出,函数fib不会一次性生成所有数据,每次循环遇到yield中断返回,并返回一次b的值
生成器的send方法’
send() 方法就是在next() 的功能之上,添加传值给 yield。
def generator2():
value1 = yield 0 ##对于有=的代码执行顺序是先执行等号右边的代码,遇到yield则暂停执行将后面 0返回
print('value1 is ', value1)
value2 = yield 1 ##对于有=的代码执行顺序是先执行等号右边的代码,遇到yield则暂停执行将后面 1返回
print('value2 is ', value2)
value3 = yield 2
print('value3 is ', value3)
g = generator2()
a=next(g) # 调用next()开始执行,返回 0
print(a)
b=g.send(2) #相当于调用next,从上次暂停的地方继续执行并将2传递给yield
print(b)
g.send(3)
用生成器模拟一个生产者-消费者场景
import time
def consumer(name):
print("%s 准备吃包子啦!" %name)
while True:
baozi = yield ##此处代码可以理解为先执行等号右边,在左边
print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
##定义一种consumer生成器
def producer():
c = consumer('A') ##为A做一个consumer生成器c
c.__next__() ##调用一次A的consumer生成器c,使用生成器时遇到yield中断返回
print("开始准备做包子啦!")
for i in range(4):
time.sleep(1)
print("做了1个包子!")
c.send(i)
##做一个循环,通过send方法调用生成器c,send方法将i的值传送给yield,在生成器中baozi的值指向yield
producer()
##输出结果如下:
A 准备吃包子啦!
开始准备做包子啦!
做了1个包子!
包子[0]来了,被[A]吃了!
做了1个包子!
包子[1]来了,被[A]吃了!
做了1个包子!
包子[2]来了,被[A]吃了!
做了1个包子!
包子[3]来了,被[A]吃了!