七、python的迭代器和生成器

迭代器

含有 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

生成器

生成器也遵循迭代器协议,所以它是迭代器的一种。
生成器的构建方式有两种:

  1. 生成器函数:常规函数定义,但使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。
  2. 生成器表达式:类似于列表推导,但生成器返回按需产生结果的一个对象,而不是一次性构建一个结果列表。

生成器表达式和生成器函数都是产生一个生成器对象,生成器对象每次取一个对象元素,并不会一次性全部输出。

因为生成器函数,是一个函数,有更强的构造性,所以基本使用生成器时,还是以生成器函数为主。

创建一个简单的生成器

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]吃了!
发布了40 篇原创文章 · 获赞 2 · 访问量 2060

猜你喜欢

转载自blog.csdn.net/weixin_42155272/article/details/93879973