python_10 迭代器和生成器

迭代器协议:

1.迭代器协议是指:对象必须提供一个next方法,执行该方法要么返回迭代中的下一项,要么就引起一个Stopiteration异常,以终止迭代(只能往后走不能往前退)

2.可迭代对象:实现了迭代器协议的对象(如何实现:对象内部定义_iter_方法)

3.协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象

for循环机制:

for循环的本质:循环所有对象,全都是使用迭代器协议。

(字符串,列表,元组,字典,集合,文件对象)这些都不是可迭代对象,只不过在for循环时,调用了他们内部的_iter_方法,把他们变成了可迭代对象

然后for循环调用可迭代对象的_next_方法去取值,而且for循环会捕捉Stopiteration异常,以终止迭代。

s='abc'
s_i=s.__iter__()
print(s_i.__next__())
print(s_i.__next__())
print(s_i.__next__())
print(s_i.__next__())#抛StopIteration异常


>>>a
>>>b
>>>c
>>>a
Traceback (most recent call last):
b
  File "E:/ch/Pylearning/Learning_09.py", line 115, in <module>
c
    print(s_i.__next__())
StopIteration

for循环的本质:

l=[1,2,3]

for i in l:#i_l=l.__iter__()  i_l.__next__()
    print(i)
#while循环完成for循环工作
s='qwer'
i=0
while i<len(s):
    print(s[i])
    i+=1

但while循环不能遍历字典、集合、文件。
能被for循环遍历的对象都有__iter__()方法。

f=open('a.txt','r+',encoding='utf-8')
iter_f=f.__iter__()
print(iter_f.__next__(),end='')
print(iter_f.__next__(),end='')
print(iter_f.__next__(),end='')
print(iter_f.__next__(),end='')

for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,在遍历之前,先调用对象的__iter__方法将起转换成一个迭代器,然后使用迭代器协议去实现循环访问,这样所有的对象就都可以通过for循环来遍历了。

用while循环模拟for循环机制:

dic={'a':1,'b':2,'c':3}
iter_d=dic.__iter__()
while True:
    try:
        print(iter_d.__next__())
    except StopIteration:
        print('迭代完成')
        break

>>>a
b
c
迭代完成

next()方法是系统自带方法,本质上也是调用__next__()方法

l=[1,2,3]
a=l.__iter__()
print(next(a))#next()————》调用a.__next__()方法

 只要遵循迭代器协议,那么该对象就是可迭代对象

生成器:

生成器:可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__()方法),所以生成器就是可迭代对象

生成器分类及在python中的表现形式:

1.生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结构。yield语句一次返回一个结果,每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行.yield相当于return,但是可以在一个函数中使用多次。yield相当于封装了__iter__()方法,直接得到生成器对象。

def test():
    yield 1
    yield 2
    yield 3
    yield 4

g=test()
print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())

>>>1
2
3
4

2.生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表

三元表达式

#三元表达式
name='ss'
#如果name='alex'返回'sb'否则返回'hand'
r='sb' if name=='alex' else 'hand'
print(r)


列表解析:

#将列表中放入10个鸡蛋
l1=[]
for i in range(1,11):
    l1.append('鸡蛋%s'%i)
print(l1)

>>>['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9', '鸡蛋10']

#列表解析的方式
l2=['鸡蛋%s'%i for i in range(1,11)]
print(l2)

>>>['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4', '鸡蛋5', '鸡蛋6', '鸡蛋7', '鸡蛋8', '鸡蛋9', '鸡蛋10']
l3=['鸡蛋%s'%i for i in range(1,11) if i<5]#没有四元表达式
print(l3)

>>>['鸡蛋1', '鸡蛋2', '鸡蛋3', '鸡蛋4']

生成器表达式:

1.把列表解析的[]换成()得到的就是生成器表达式

2.列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存

l2=['鸡蛋%s'%i for i in range(1,11)]
#生成器:
l4=('鸡蛋%s'%i for i in range(1,11))
print(l4)
print(l4.__next__())
print(l4.__next__())
print(next(l4))
print(next(l4))

>>><generator object <genexpr> at 0x000001FFD4A3C1A8>
鸡蛋1
鸡蛋2
鸡蛋3
鸡蛋4

3.pyhton不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如:sum函数是python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:

l=[1,2,3,4,5,6,7,8,9,7,4,6,4,1]

print(sum([1,2,3,4,5,6]))
print(sum(i for i in range(0,1000000)))#生成器表达式方式


 

yield:

使用第一次__next__()则表示函数运行到第一个yield,使用第二次__next__()则表示函数从第一个yield运行到第二个yield,以此类推。。。

import time


def test():
    print('开始生孩子了')
    yield ''
    time.sleep(3)
    print('又开始生孩子了')
    yield '我的孩子'
    time.sleep(3)
    print('又又开始生孩子了')
    yield '我的孩子的孩子'

res=test()

print(res.__next__())
print(res.__next__())
print(res.__next__())

>>>开始生孩子了
我
>>>又开始生孩子了
我的孩子
>>>又又开始生孩子了
我的孩子的孩子

 生成器函数:

例1:

def product_baozi():
    for i in range(1,100):
        print('生产包子中')
        yield '包子%s'%i
        print('卖包子')
        

p=product_baozi()
baozi1=p.__next__()
print(baozi1)
baozi2=p.__next__()
print(baozi2)

>>>生产包子中
包子1
卖包子

例2:

def product_egg():
    for i in range(1,1000):
        yield '第%s个鸡蛋'%i

p=product_egg()
print(p.__next__())
print(p.__next__())
print(p.__next__())
print(p.__next__())
print(p.__next__())
print(p.__next__())
print(p.__next__())
print(p.__next__())


>>>第1个鸡蛋
第2个鸡蛋
第3个鸡蛋
第4个鸡蛋
第5个鸡蛋
第6个鸡蛋
第7个鸡蛋
第8个鸡蛋

for循环遍历生成器:

def product_egg():
    for i in range(1,1000):
        yield '第%s个鸡蛋'%i

p=product_egg()

for i in product_egg():
    print(i)

生成器总结:

 

def get_populartion():
    with open('xxx','r',encoding='utf-8') as f:
        for i in f :
            yield i

p=get_populartion()
print('全国总人口约为%d'%sum(eval(i)['人口'] for i in p ))

def test():
    for i in range(0,4):
        yield i

t=test()
# for i in t:
#     print(i)
t1=(i for i in t)
t2=(i for i in t1)
print(list(t1))
print(list(t2))

>>>[0, 1, 2, 3]
>>>[]


补充
send()用法,send与next和__next__()都可以使生成器往下执行
yield 2相当于return,控制的是函数的返回值
x=yield的另外一个特性,接收send传过来的值,赋值给x
# send()用法,send与next和__next__()都可以使生成器往下执行
# yield 2相当于return,控制的是函数的返回值
# x=yield的另外一个特性,接收send传过来的值,赋值给x
def test():
    print('开始第一次')
    first=yield 1
    print('开始第二次',first)
    yield 2
    print('开始第三次')
    yield 3

t=test()
print(t.__next__())

t1=t.send('啊啊啊啊')
print(t1)


>>>开始第一次
1
开始第二次 啊啊啊啊
2

生产者消费者模型:

#两个函数并行:
#生产者消费者模型
def consumer(name):
    print('%s准备吃包子了'%(name))
    time.sleep(0.5)
    while True:
        g=yield
        print('%s开始吃%s包子了'%(name,g))

def  producer():
    p1=consumer('alex')
    p1.__next__()
    for i in range(1,10):
        time.sleep(0.5)
        p1.send('第%d'%i)

producer()


>>>alex准备吃包子了
alex开始吃第1包子了
alex开始吃第2包子了
alex开始吃第3包子了
alex开始吃第4包子了
alex开始吃第5包子了
alex开始吃第6包子了
alex开始吃第7包子了
alex开始吃第8包子了
alex开始吃第9包子了
原文地址:https://www.cnblogs.com/Manuel/p/10577466.html