一、可迭代对象和迭代器
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))
输出
121
如下图所示
这里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
输出
123
每次调用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))
输出
TrueTrueTrueTrueTrueTrueFalseFalseFalseFalseFalseTrue
可迭代对象:只有__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...01234
2.生成器函数
- 生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行;
普通函数return返回
def lay_eggs(num): egg_list=[] for egg in range(num): egg_list.append('蛋%s' %egg) return egg_listyikuangdan=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__()) #迭代 蛋0print(laomuji.__next__()) #蛋1print(laomuji.__next__()) #蛋2egg_l=list(laomuji) 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):#生成器对象 File "/Users/hexin/PycharmProjects/py3/day5/2.py", line 71, in 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'))
输出
hexin ready to eathexin 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 eathexin start to eat food1['food1']hexin start to eat food2['food1', 'food2']hexin start to eat food3['food1', 'food2', 'food3']