迭代器
可迭代对象
有iter()方法的就是可迭代对象,列如列表,字典,元祖,集合,文件对象等都是
d = {'key1':1,'key2':2,'key3':3}
res = d.__iter()__
print(res)
>> <dict_keyiterator object at 0x000321312042143132W>
res.__next__()
res.__next__()
res.__next__()
res.__next__()
>>>
key1
key2
key3
报错StopIteration
while True:
try:
res.__next()__
except StopIteration as e:
break
>>> key1,key2,key3
迭代器对象
内置有next()方法并且还内置有iter()方法的对象
调用next()方法,就会得到迭代器的下一个值
调用iter()方法,就会得到迭代器本身,那为啥还需要iter()方法呢,就是为了要和可迭代对象的for方法保持一致
for 方法的执行步骤:
- 调用对象的iter()方法,得到它的一个迭代器对象
- 调用该迭代器的next()方法,拿到一个返回值,赋值给key
循环执行第二步直到抛出StopIteration异常,for循环捕捉到异常并结束循环
x = ['dasd','java','python']
y = iter(x)
print(next(y))
print(next(y))
print(next(y))
一旦迭代器用完了(也就是说,完全迭代了),就没有什么可以迭代的了,所以第二次迭代不会产生任何结果
a = map(int,['1','2','3'])
for i in a:
print(i,end=',')
for i in a:
print(i,end=',')
>>>>1,2,3
生成器(自定义的迭代器)
定义
列表所有数据都在内存中,如果有海量数据的话将会非常耗内存。如果列表元素按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。
生成器是一个特殊的程序,可以被用作控制循环的迭代行为,python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。
生成可迭代的对象 : 节省空间和时间创建
把[]改成()
使用yield关键字
def gen(n):
for i in range(n):
yield i*i
#return i*i 如果是return,当n=0是直接就返回了
x = gen(5)
for i in x:
print(x,end=' ')
工作原理
生成器(generator)能够迭代的关键是它有一个next()方法,工作原理就是通过重复调用next()方法,直到捕获一个异常,
可用next()调用生成器对象来取值。next 两种方式 t.next() | next(t)。可用for 循环获取返回值(每执行一次,取生成器里面一个值)
- yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield的下一条语句开始执行。
- .send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停),但send()能传一个值,这个值作为yield表达式整体的结果,换句话说,就是send可以强行修改上一个yield表达式值。比如函数中有一个yield赋值,a = yield 5,第一次迭代到这里会返回5,a还没有赋值。第二次迭代时,使用.send(10),那么,就是强行修改yield 5表达式的值为10,本来是5的,那么a=10
def test():
i = 0
while i < 5:
temp = yield i
print('temp is %s'%temp)
i += 1
a = test() #a是一个生成器
a.__next__() #-->a.send(None),相当于发送一个None给生成器,此时i = 0,停留在yield
a.__next__()
a.__next__()
a.send('hello')
a.__next__()
a.__next__() #i > 5了,此时生成器取完了,打印StopIteration错误
>>>>>
0 返回i= 0,不打印后面的print
temp is None 返回i=1
1
temp is None 返回i=2
2
temp is hello 返回i=3
3
temp is None 返回i=4
4
Traceback (most recent call last):File "<stdin>", line 1, in <module> StopIteration
#第一次取值:yield 返回了 i 值 0,停在yield i,temp没赋到值。
第二次取值,开始在print,temp没被赋值,故打印None,i加1,继续while判断,
yield 返回了 i 值 1,停在yield i
生成斐波拉契数列:
def fib(max):
n,a,b = 0,0,1
while n < max:
yield b
a,b = b,a+b #a,b = b ,a+b其实相当于t =a+b ,a =b ,b =t
n += 1
return 'done'
for i in fib(6):
print(i,end=' ')
>>>>1 1 2 3 5 8
#可以看出这个打印并不返回return的'done',如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value
g = fib(7)
while True:
try:
x = g.__next__()
print(x,end=' ')
except Exception as e:
print(e.value)
break
>>>1 1 2 3 5 8 13 done
yield单线程并发
二维列表转化为一维列表import time
def consumer(name):
print(f'{name} 准备学习啦!')
while True:
lesson = yield
print(f'开始[{lesson}],[{name}]老师来讲课了')
def producer():
c1 = consumer('A')
c2 = consumer('B')
c1.__next__()
c2.__next__()
print('开始上课了')
for i in range(10):
time.sleep(1)
print('到了2个同学')
c1.send(i)
c2.send(i)
if __name__ == '__main__':
producer()
如果是多维列表转化a = [[1,2,3],[4,3,2],[1,2,34,5,6]]
num_list = []
def handlelist():
for num in a:
for i in num:
yield i*i
print(list(handlelist))
>>[1, 4, 9, 16, 9, 4, 1, 4, 1156, 25, 36]
a = [1,2,3,[1,2,3,[1,2,[232,1,1,2]]],[[3,3,2],4,3,2],[1,2,34,5,6]]
def enumlist(a):
try:
for num in a:
for i in enumlist(num):
yield i
except:
yield a
print(list(enumlist(a)))
>>[1, 2, 3, 1, 2, 3, 1, 2, 232, 1, 1, 2, 3, 3, 2, 4, 3, 2, 1, 2, 34, 5, 6]