迭代器
1.什么叫迭代?
使用for循环遍历取值的过程,叫做迭代
2.什么是可迭代对象?
能够使用for遍历取值的对象,叫可迭代对象
可迭代对象:str、list、set、tuple 、dict
不可迭代对象: int 、float、class
3.什么是迭代器?
迭代器是一个可以记住遍历的位置的对象
①迭代器对象从集合的第一个元素开始访问,直到所有元素被访问完
②迭代器只能往前不会后退
4.如何判断一个对象是不是可迭代对象、迭代器?
①
from collections import Iterable
from collections import Iterator
isinstance( obj, Iterable ) #obj是不是可迭代对象
isinstance( obj, Iterator) #判断obj是不是迭代器?
②
一个实现了iter方法的对象是可迭代的,一个实现next方法并且是可迭代的对象是迭代器。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
5.如何创建迭代器
方法一:直接在可迭代对象(字符串,列表或元组对象)上创建迭代器
方法二:把一个类作为一个迭代器使用, 需要在类中实现两个方法 iter() 与 next() .
①__iter__() 方法:返回一个特殊的迭代器对象, 这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成。
②__next__() 方法:(Python 2 里是 next())会返回下一个迭代器对象。
用法
#1. 创建迭代器 ,iter()函数
通过iter()函数创建可迭代对象(比如:列表、元组、字符串)的迭代器.(字符串,列表或元组对象都可用于创建迭代器)
#2. 遍历取值
迭代器对象可以使用常规 for 语句进行遍历,
也可以使用next()函数来取值。iter()函数实际上就是调用了可迭代对象的 __iter__ 方法
生成器
1. 什么是生成器?
生成器是一种特殊的迭代器(一个没有__iter__和__next__方法的特殊迭代器),不存储数据,存储生成数据的方式
2.为什么需要引入生成器?
当需要生成好多数据时,如果一下全部产生,将会占用好多内存。
eg:
创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了.
此时引入生成器,需要多少生成多少,什么时候需要什么时候继续,而不用一下子全部产生。
3.生成器特点(节省空间,暂停程序,需要时再继续执行):
①一个没有__iter__和__next__方法的特殊迭代器;
②函数只执行一部分就返回;
③可以让一个函数暂停执行,并且保存上次的值,根据上次的值恢复到原来的样子,再做接下来的操作;
④迭代器节省空间,实现循环;
⑤生成器可以让一个看起来像函数的代码暂停执行,并根据自己的想法调用next/send继续执行;
4.生成器的2种构建方式
1.将列表推导式的小括号换成中括号;
2.函数中使用yield
# 创建生成器模板
一个函数中yield,此时这个函数不再是函数,而是生成器的模板
#创建生成器对象
当调用一个函数时,发现该函数中有yield,那么不再事调用函数,而是创建一个生成器对象
5.生成器执行流程
当第一次调用for/next执行时,会从生成器的第一行开始依次向下执行,直到在循环中碰见yield,就会返回yield后面的变量/字符;
然后第二次调用for/next时,就会从上次的yield后面的代码继续执行,直到在循环中再次碰到yield,返回;依次往下,直到没有了数据。
6.生成器、函数执行流程的区别
函数是顺序执行,遇到return语句或者最后一行函数语句就返回。
而generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
7.生成器启动方式
①可以使用 for i in 生成器对象 来遍历生成器中的数据,__ next__
②也可以用 next(生成器对象) 来一个一个获取生成器中的值(调用next()运行生成器,并 返回yield的值);
③通过send()来启动生成器,取里生成器里面的值(即返回yield的值)如下代码
注意:一般 生成器对象.send(None) 不会放到第一次启动生成器,如果非要这么做,要传入None
使用yield完成斐波那契数列
def Fibonacci(n):
a, b = 0, 1
count_num = 0
while count_num < n:
# 如果函数中有一个yield语句,那么这个就不再是函数,而是一个生成器的模板
yield a
a, b = b, a+b
count_num += 1
# 如果在调用时发现这个函数中有yield,那么此时,不是调用函数,而是创建一个生成器对象
fb = Fibonacci(5)
print("使用for循环遍历生成器中的所有数字".center(40, "-"))
for i in fb:
print(i)
print("使用next依次生成三次数字".center(40, "-"))
print(next(fb))
print(next(fb))
print(next(fb))
send()启动生成器
def Fibonacci(n):
a, b = 0, 1
count_num = 0
while count_num < n:
ret = yield a
print("ret:", ret)
a, b = b, a+b
count_num += 1
fb = Fibonacci(5)
print(next(fb))
#send的参数会传给 yield a ,当做yield a的结果,并赋值给ret
#send的结果是 :下次调用 yield时,yield后面a的值
print(fb.send("haha"))
print(next(fb))
# 0
# ret: haha
# 1
# ret: None
# 1
(我们可以理解为,第一次使用next,先执行等号右边的代码,就将yield a返回给了next(fb);
然后下次调用send时,执行等号左边的,将send的传值赋值给ret,再执行后续代码;
或者我们可以理解 ret = yield a 为两步
===>1.yield a;
2.ret = arg;其中的arg表示send的传值,如果不传值,默认为None,所以当next在send后面调用时,就默认传了None;)