迭代器与生成器
最后更新时间:
文章总字数:
预计阅读时间:
一.迭代器(iterator)
1.1 迭代器创建
迭代器只能往前取值,不会后退
用iter函数可以将一个可迭代对象变成一个可迭代对象的迭代器
next函数可以获取迭代器的下一个值,取决于上一个next
1 |
|
结果:
1 |
|
next用法:
1 |
|
结果:
1 |
|
还有一个容易疏漏的点,next会固定当前迭代器中元素的角标,用for循环的话会直接从上一个next取的元素的下一个元素开始循环。说白了,for循环就是更多次数的next。迭代器是不可后退的,next或者for循环迭代完后再用next会报错,再用for里面的代码不会运行。
而其他可迭代对象其实用for时运用了魔术方法,将其返回个迭代器,每for一次就调用一次魔术方法,所以这些可迭代对象(如列表)可以反复使用for循环。
1 |
|
结果:
1 |
|
1.2 自定义迭可代对象
1 |
|
结果:
1 |
|
自定义了iter
魔术方法后,对象变成了一个可迭代对象,for循环会调用__next__
方法,每次迭代都会返回自定义的返回值。由此可见,尽管是数字,只要自定义了iter
魔术方法就可以让它变成一个可迭代对象。而next的返回值自定义后会作为每次迭代的返回值。for循环就很容易陷入死循环,因为next没有设置打断的条件。
接下来看看自定义next的使用:
1 |
|
结果:
1 |
|
可见设置打断条件后for循环就会被打断,就跳出死循环了。而最后一块代码,我想说明的是,因为我们在next函数中每次迭代都让a++,不管是转换为迭代器还是就用b对象,实际上都是一个对象。其中的a值可能早就已经超出打断条件,因此无法继续迭代。
正确用法:
1 |
|
结果:
1 |
|
因为每次超出迭代范围后,跟迭代有密切相关的 self.x 被重置,因此就可以反复使用for循环了。但其本质还是只有一个对象,因此转换后的迭代器与对象a同时进行迭代时,这俩的进度是一样的,因为同一个对象a中 self.x 进度一致。或者也可以重新创建一个对象再进行迭代。
1.3 迭代器大题
1 |
|
这个问题就类似日后会解决的机器学习中的处理图片,分批次处理而不是一次性传入。
下面是解答:
1 |
|
结果:
1 |
|
最关键的就是通过切片返回一个列表,利用设置的batch_size进行操作,使得这个列表返回的元素都是新的10个。
二.生成器(Generator)
是一种特殊的迭代器,通过函数定义,用 yield 语句生成值。生成器可以自动实现迭代协议,无需手动实现 iter() 和 next()
比手动实现迭代器更易写
惰性计算:生成器在每次调用 next() 时生成一个值,而不是一次性生成所有值
yield:暂停函数执行并其后的表达式结果,保留函数的状态,以便下一次继续执行
1 |
|
结果:
1 |
|
由此可见,虽然生成器中没有 iter 和 next 方法,但是实际上,其是自动生成的。而且生成器天生就是一个迭代器,可以使用 next 方法。yield 出现时,其后的代码都暂时不执行,只返回yield 后面的值,当再次(进行迭代)调用 next 时,从 yield 后面的代码开始执行,直到遇到下一个 yield。