最近在使用keras,发现yield频繁出现,在此做个yield有关知识的总结。(主要个人理解,以通俗易懂为主,不一定专业)
1、迭代器
区别于常见的list、tuple、dict等容器(直接将数据存放于内存中),迭代器在内存中仅仅存放产生数据的逻辑(实际数据在运行时动态生成)。
得到迭代器的几种方式:
(1)使用iter函数,可将常见容器转为迭代器,eg iter([1,2,3,4,5,6])。
(2)重写__iter__函数(可以省略)和__next__函数(必须有),自定义迭代器类,实例化得到迭代器对象,如下所示:
class MyIter(): def __init__(self): *** pass def __iter__(self): return self def __next__(self): *** raise StopIteration *** return ***
2、生成器
生成器(generator)是一种特殊的迭代器,具备延迟计算性质,一般和yield关键字挂钩,即通过yield关键字得到生成器函数,生成器在使用时与迭代器类似,数据在运行中动态生成,直至全部数据生成为止。
eg:
def stus(): name = yield "zhangsan" print("name = {}".format(name)) name = yield "lisi" print("name = {}".format(name)) name = yield "wangwu" print("name = {}".format(name)) yield "last" if __name__ == '__main__': s1 = stus() print(next(s1)) print("") print(s1.send("-- lu --")) print("") print(s1.__next__()) print("") print(s1.send("-- qiao --"))
上述代码运行结果如下:
zhangsan
name = -- lu --
lisiname = None
wangwuname = -- qiao --
last
生成器既可以包含有限元素(如上所示),也可以包含无限元素(比如keras的数据产生机制【data_generator函数】)的。
(1)next(s1)等价于s1.send(None)
(2)生成器的send函数的作用:用于代表yield ***表达式,进行赋值操作【一般情况yield所在行是一个赋值表达式】。,比如上述的name = yield "zahngsan",s1.send("xixi"),那么"xixi"赋值给了name,同时获取下一个yield后面表达式的值。
(3)在执行next或send函数之后,程序会"卡住"在yield **表达式行(yield关键字所在行,而非下一行,等待被send替换赋值或next直接跳过),不会继续执行,直至下一个next函数或send函数调用
(4)使用生成器表达式(区别于列表推导式,eg:[i for i in range(5)],这里是方括号)也可以产生generator:(i * 10 for i in range(3)),这里是圆括号。
(5)若迭代器已访问到末尾,仍继续访问,则会抛出如下:
main()
File "D:/zzc_learning/PycharmWorkspace/python_learning/yield_code.py", line 60, in main
print(g.send(20))
StopIteration
(6)生成器和迭代器均只能访问一次,重复访问会报错或获取不到数据,因为产生数据的逻辑代码已执行结束!
(7)和return的区别:return会停止和退出当前函数/代码块的执行,并返回给调用者;yield不会停止和退出当前代码,只是"卡住"等待下一次被执行!
-- over --