迭代器和生成器

一、可迭代对象和迭代器
1.迭代的概念
上一次输出的结果为下一次输入的初始值,重复的过程称为迭代,每次重复即一次迭代,并且每次迭代的结果是下一次迭代的初始值 


注:循环不是迭代


while True: #只满足重复,因而不是迭代
     print('====>')
 


2.可迭代的对象
内置__iter__方法的,都是可迭代的对象。
list是可迭代对象,dict是可迭代对象,set也是可迭代对象。


[1,2].__iter__()
'hello'.__iter__()
(1,2).__iter__()


{'a':1,'b':2}.__iter__()
{1,2,3}.__iter__()
 


例如:


复制代码
x = [1, 2, 3]
y = iter(x)
z = iter(x)
print(next(y))
print(next(y))
print(next(z))
print(type(x))
print(type(y))
复制代码
输出


1
2
1
<class 'list'>
<class 'list_iterator'>
  


 


如下图所示


 


这里x是一个可迭代对象,y和z是两个独立的迭代器,迭代器内部持有一个状态,该状态用于记录当前迭代所在的位置,以方便下次迭代的时候获取正确的元素。


迭代器有一种具体的迭代器类型,比如list_iterator,set_iterator。可迭代对象实现了__iter__方法,该方法返回一个迭代器对象。


 


3.迭代器
1.为什么要有迭代器?
对于没有索引的数据类型,必须提供一种不依赖索引的迭代方式。


 


2.迭代器定义:
迭代器:可迭代对象执行__iter__方法,得到的结果就是迭代器,迭代器对象有__next__方法


它是一个带状态的对象,他能在你调用next()方法的时候返回容器中的下一个值,任何实现了__iter__和__next__()方法的对象都是迭代器,__iter__返回迭代器自身,__next__返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常


 


3.迭代器的实现
例:


复制代码
i=[1,2,3].__iter__()  


print(i)    #迭代器


print(i.__next__())
print(i.__next__())
print(i.__next__())
#print(i.__next__()) #抛出异常:StopIteration
复制代码
输出


<list_iterator object at 0x1019c3eb8>
1
2
3
每次调用next()方法的时候做两件事: 


为下一次调用next()方法修改状态
为当前这次调用生成返回结果
迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。


 


4.如何判断迭代器对象和可迭代对象
复制代码
from collections import Iterable,Iterator
'abc'.__iter__()
().__iter__()
[].__iter__()
{'a':1}.__iter__()
{1,2}.__iter__()


f=open('a.txt','w')
f.__iter__()


#判断是否为可迭代对象,以下都是
print(isinstance('abc',Iterable))
print(isinstance([],Iterable))
print(isinstance((),Iterable))
print(isinstance({'a':1},Iterable))
print(isinstance({1,2},Iterable))
print(isinstance(f,Iterable))


#判断是否为迭代器,只有文件是
print(isinstance('abc',Iterator))
print(isinstance([],Iterator))
print(isinstance((),Iterator))
print(isinstance({'a':1},Iterator))
print(isinstance({1,2},Iterator))
print(isinstance(f,Iterator))
复制代码
输出


复制代码
True
True
True
True
True
True
False
False
False
False
False
True
复制代码
 


可迭代对象:只有__iter__方法,执行该方法得到的迭代器对象


迭代器:有__iter__和__next__()方法


注:对于迭代器对象来说,执行__iter__方法,得到的结果仍然是它本身


 


5.迭代器的优点和缺点
优点:
1.提供了一种不依赖下标的迭代方式
2.就跌迭代器本身来说,更节省内存


缺点:
1. 无法获取迭代器对象的长度

2. 不如序列类型取值灵活,是一次性的,只能往后取值,不能往前退



二、生成器
1.定义
生成器(generator)是一个特殊的迭代器,它的实现更简单优雅,yield是生成器实现__next__()方法的关键。它作为生成器执行的暂停恢复点,可以对yield表达式进行赋值,也可以将yield表达式的值返回。


也就是说,yield是一个语法糖,内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态。


 


yield的功能:
1.相当于为函数封装好__iter__和__next__
2.return只能返回一次值,函数就终止了,而yield能返回多次值,每次返回都会将函数暂停,下一次next会从上一次暂停的位置继续执行


例:


复制代码
def counter(n):
    print('start...')
    i=0
    while i < n:
        yield i
        i+=1
    print('end...')




g=counter(5)
print(g)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
# print(next(g))   #会报错
复制代码
输出


start...
0
1
2
3
4
 


 


2.生成器函数
生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行;
 


普通函数return返回


复制代码
def lay_eggs(num):
    egg_list=[]
    for egg in range(num):
        egg_list.append('蛋%s' %egg)
    return egg_list


yikuangdan=lay_eggs(10) #我们拿到的是蛋
print(yikuangdan)
复制代码
输出


['蛋0', '蛋1', '蛋2', '蛋3', '蛋4', '蛋5', '蛋6', '蛋7', '蛋8', '蛋9']
 


迭代器函数


复制代码
def lay_eggs(num):
    for egg in range(num):
        res='蛋%s' %egg
        yield res       #生成器关键语法
        print('下完一个蛋')


laomuji=lay_eggs(10) #我们拿到的是一只母鸡
print(laomuji)
print(laomuji.__next__())       #迭代  蛋0
print(laomuji.__next__())     #蛋1
print(laomuji.__next__())       #蛋2
egg_l=list(laomuji)              #egg_I是一个列表, egg_I=['蛋3','蛋4','蛋5','蛋6','蛋7','蛋8','蛋9']
print(egg_l)
复制代码
输出


复制代码
蛋0
下完一个蛋
蛋1
下完一个蛋
蛋2
下完一个蛋
下完一个蛋
下完一个蛋
下完一个蛋
下完一个蛋
下完一个蛋
下完一个蛋
下完一个蛋

['蛋3', '蛋4', '蛋5', '蛋6', '蛋7', '蛋8', '蛋9']


3.生成器表达式
生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表;
food=yield food_list
#g.send('food1'),先把food1传给yield,由yield赋值给food,然后返回给food_list,然后再往下执行,直到再次碰到yield,然后把yield后的返回值返回给food_list







注意:开始生成器不能send非空值


复制代码
def eater(name):        #协程函数
    print('%s ready to eat' %name)
    food_list=[]
    while True:
        food=yield food_list           #装饰器表达式
        food_list.append(food)
        print('%s start to eat %s' %(name,food))




g=eater('hexin')
print(g)        #生成器


print(g.send('food1'))  #传值
复制代码
输出


Traceback (most recent call last):
<generator object eater at 0x1049030f8>    #生成器对象
  File "/Users/hexin/PycharmProjects/py3/day5/2.py", line 71, in <module>
    print(g.send('food1'))
TypeError: can't send non-None value to a just-started generator    #开始生成器不能send非空值
 


初始化后
复制代码
def eater(name):        #协程函数
    print('%s ready to eat' %name)
    food_list=[]
    while True:
        food=yield food_list           #装饰器表达式
        food_list.append(food)
        print('%s start to eat %s' %(name,food))




g=eater('hexin')
print(g)        #生成器
next(g) #等同于 g.send(None),初始化


print(g.send('food1'))
复制代码
输出


<generator object eater at 0x107cde258>
hexin ready to eat
hexin start to eat food1
['food1']
 


为了防止忘记初始化,可利用装饰器进行初始化,如下
复制代码
def deco(func):     #初始化函数
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        next(res)          #等同于 g.send(None),初始化
        return res
    return wrapper


@deco       #用初始化函数装饰器,调用初始化函数
def eater(name):        #协程函数
    print('%s ready to eat' %name)
    food_list=[]
    while True:
        food=yield food_list           #装饰器表达式
        food_list.append(food)
        print('%s start to eat %s' %(name,food))




g=eater('hexin')
# print(g)        #生成器
# next(g) #等同于 g.send(None),初始化


print(g.send('food1'))
print(g.send('food2'))
print(g.send('food3'))
复制代码
输出


复制代码
hexin ready to eat
hexin start to eat food1
['food1']
hexin start to eat food2
['food1', 'food2']
hexin start to eat food3
['food1', 'food2', 'food3']



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值