本片博客是https://www.cnblogs.com/wj-1314/p/8490822.html的总结,内容不是很完备,这篇博客写得非常详细。
生成器
生成器动态生成元素,节省内存。
一共有两种创建方式,第一种通过产生式创建,第二种通过yield关键字创建。
它叫做生成器函数,通过祖塞、唤醒来实现动态生成元素。
yield用法如下:
#表示n是我们想要生成的元素
def f1():
yield n
#表示n是我们通过生成器传入的值
def f2():
n=yield
对于一个生成器函数,执行顺序是
# 函数有了yield之后,函数名+()就变成了生成器
# return在生成器中代表生成器的中止,直接报错
# next的作用是唤醒并继续执行
# send的作用是唤醒并继续执行,发送一个信息到生成器内部
'''生成器'''
def create_counter(n):
print("create_counter")
while True:
yield n
print("increment n")
n +=1
gen = create_counter(2)
print(gen)
print(next(gen))
print(next(gen))
结果:
<generator object create_counter at 0x0000023A1694A938>
create_counter
2
increment n
3
Process finished with exit code 0
第一次调用next方法,程序会从开始执行,然后执行完yield n后就祖塞掉,然后再次调用next函数后,从yield下一句开始继续执行。
那么返回值怎么办得到呢?因为我们的函数状态是被保存的,当执行完所有要得到的数据后,就会抛出StopIteration错误,这个错误就包含返回值,这个只有手动调用next方法能够得到,而使用for循环得不到返回值。
生成器和迭代器是Iterable对象,其实生成器就是一种迭代器。
迭代器
一个实现了iter方法的对象时可迭代的(Iterable对象),一个实现next方法的对象是迭代器(Iterator)
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
生成器都是Iterator对象,但list、dict、str虽然是Iterable(可迭代对象),却不是Iterator(迭代器)。
你可能会问,为什么list、dict、str等数据类型不是Iterator?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。所以像filter这个函数得到的Filter对象就是一个迭代器。iter函数可以将可迭代对象转换为迭代对象,或者可迭代对象的__iter__方法。
小结
- 凡是可作用于for循环的对象都是Iterable类型;
- 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
- 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
- 在Iterator对象中才存在__next__方法,其实类里面的带__的前缀后缀的方法都是与完全不带这个前缀后缀的全局方法是功能相同比如dir与__dir__、next与__next__方法。Iterable对象没有这个方法。for循环就是现将Iterable对象转为Iterator对象,然后不断调用next方法。
- 生成器(generator)能够迭代的关键是他有next()方法,工作原理就是通过重复调用next()方法,直到捕获一个异常。
- 带有yield的函数不再是一个普通的函数,而是一个生成器generator,可用于迭代
- yield是一个类似return 的关键字,迭代一次遇到yield的时候就返回yield后面或者右面的值。而且下一次迭代的时候,从上一次迭代遇到的yield后面的代码开始执行
- send()和next()都有返回值,他们的返回值是当前迭代遇到的yield的时候,yield后面表达式的值,其实就是当前迭代yield后面的参数。
- 对于生成器函数来说,如果next遇到a=yield那么会柱塞掉,然后等待接收send函数传入的值,如果这时候调用send那么程序从a=yield继续执行直到再次遇到a=yield,祖塞掉,生成器第一次运行时必须使用next函数,next函数与send(None)的作用是一样的(一样的作用),在a = yield和yield a都可以使用send(None)表示__next__,只是在a=yield时使用next传入的是None。