php可以做网站布局吗,网站建设海报素材图片,万网域名续费查询,国内网页做的好看的网站在学习python的时候#xff0c;刚开始接触生成器#xff08;generator#xff09;这个概念的时候#xff0c;其实还是不太能理解#xff0c;感觉并没有完全掌握#xff0c;今天看到这篇文章的时候#xff0c;感觉对这个概念真的是有了进一步的了解#xff0c;感觉生成器…在学习python的时候刚开始接触生成器generator这个概念的时候其实还是不太能理解感觉并没有完全掌握今天看到这篇文章的时候感觉对这个概念真的是有了进一步的了解感觉生成器和列表解析的关系似乎有点类似于range和xrange函数的关系一样列表解析是将要处理得到的序列都先生成了而生成器是要通过多次迭代才会生成整个序列否则每次执行就只是生成其中一个此外函数中出现了yield关键字该函数就是生成器函数了。
这篇文章转载自深入理解Python中的生成器另外也可以看看廖雪峰老师的教程。
生成器(generator)概念 生成器不会把结果保存在一个系列中而是保存生成器的状态在每次进行迭代时返回一个值直到遇到StopIteration异常结束。
生成器语法 1. 生成器表达式 通列表解析语法只不过把列表解析的[]换成() 生成器表达式能做的事情列表解析基本都能处理只不过在需要处理的序列比较大时列表解析比较费内存。 gen (x**2 for x in range(5))gen
generator object genexpr at 0x0000000002FB7B40for g in gen:
... print(g, end-)
...
0-1-4-9-16-for x in [0,1,2,3,4,5]:
... print(x, end-)
...
0-1-2-3-4-5-
生成器函数 在函数中如果出现了yield关键字那么该函数就不再是普通函数而是生成器函数。 但是生成器函数可以生产一个无线的序列这样列表根本没有办法进行处理。 yield 的作用就是把一个函数变成一个 generator带有 yield 的函数不再是一个普通函数Python 解释器会将其视为一个 generator。
下面为一个可以无穷生产奇数的生成器函数。
def odd():n1while True:yield nn2
odd_num odd()
count 0
for o in odd_num:if count 5: breakprint(o)count 1
当然通过手动编写迭代器可以实现类似的效果只不过生成器更加直观易懂
class Iter:def __init__(self):self.start-1def __iter__(self):return selfdef __next__(self):self.start 2 return self.start
I Iter()
for count in range(5):print(next(I))
题外话 生成器是包含有iter()和next()方法的所以可以直接使用for来迭代而没有包含StopIteration的自编Iter来只能通过手动循环来迭代。 from collections import Iterablefrom collections import Iteratorisinstance(odd_num, Iterable)
Trueisinstance(odd_num, Iterator)
Trueiter(odd_num) is odd_num
Truehelp(odd_num)
Help on generator object:odd class generator(object)| Methods defined here:|| __iter__(self, /)| Implement iter(self).|| __next__(self, /)| Implement next(self).
…… 看到上面的结果现在你可以很有信心的按照Iterator的方式进行循环了吧
在 for 循环执行时每次循环都会执行 fab 函数内部的代码执行到 yield b 时fab 函数就返回一个迭代值下次迭代时代码从 yield b 的下一条语句继续执行而函数的本地变量看起来和上次中断执行前是完全一样的于是函数继续执行直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次每次中断都会通过 yield 返回当前的迭代值。
yield 与 return 在一个生成器中如果没有return则默认执行到函数完毕时返回StopIteration def g1():
... yield 1
...gg1()next(g) #第一次调用next(g)时会在执行完yield语句后挂起所以此时程序并没有执行结束。
1next(g) #程序试图从yield语句的下一条语句开始执行发现已经到了结尾所以抛出StopIteration异常。
Traceback (most recent call last):File stdin, line 1, in module
StopIteration如果遇到return,如果在执行过程中 return则直接抛出 StopIteration 终止迭代。 def g2():
... yield a
... return
... yield b
...gg2()next(g) #程序停留在执行完yield a语句后的位置。
anext(g) #程序发现下一条语句是return所以抛出StopIteration异常这样yield b语句永远也不会执行。
Traceback (most recent call last):File stdin, line 1, in module
StopIteration
如果在return后返回一个值那么这个值为StopIteration异常的说明不是程序的返回值。 生成器没有办法使用return来返回值。 def g3():
... yield hello
... return world
...gg3()next(g)
hellonext(g)
Traceback (most recent call last):File stdin, line 1, in module
StopIteration: world
生成器支持的方法 help(odd_num)
Help on generator object:odd class generator(object)| Methods defined here:......| close(...)| close() - raise GeneratorExit inside generator.|| send(...)| send(arg) - send arg into generator,| return next yielded value or raise StopIteration.|| throw(...)| throw(typ[,val[,tb]]) - raise exception in generator,| return next yielded value or raise StopIteration.
close() 手动关闭生成器函数后面的调用会直接返回StopIteration异常。 def g4():
... yield 1
... yield 2
... yield 3
...gg4()next(g)
1g.close()next(g) #关闭后yield 2和yield 3语句将不再起作用
Traceback (most recent call last):File stdin, line 1, in module
StopIteration
send() 生成器函数最大的特点是可以接受外部传入的一个变量并根据变量内容计算结果后返回。 这是生成器函数最难理解的地方也是最重要的地方实现后面我会讲到的协程就全靠它了。
def gen():value0while True:receiveyield valueif receivee:breakvalue got: %s % receiveggen()
print(g.send(None))
print(g.send(aaa))
print(g.send(3))
print(g.send(e))
执行流程
通过g.send(None)或者next(g)可以启动生成器函数并执行到第一个yield语句结束的位置。 此时执行完了yield语句但是没有给receive赋值。 yield value会输出初始值0 注意在启动生成器函数时只能send(None),如果试图输入其它的值都会得到错误提示信息。通过g.send(‘aaa’)会传入aaa并赋值给receive然后计算出value的值并回到while头部执行yield value语句有停止。 此时yield value会输出”got: aaa”然后挂起。通过g.send(3)会重复第2步最后输出结果为”got: 3”当我们g.send(‘e’)时程序会执行break然后推出循环最后整个函数执行完毕所以会得到StopIteration异常。 最后的执行结果如下
0
got: aaa
got: 3
Traceback (most recent call last):
File h.py, line 14, in moduleprint(g.send(e))
StopIteration
throw() 用来向生成器函数送入一个异常可以结束系统定义的异常或者自定义的异常。 throw()后直接跑出异常并结束程序或者消耗掉一个yield或者在没有下一个yield的时候直接进行到程序的结尾。
def gen():while True: try:yield normal valueyield normal value 2print(here)except ValueError:print(we got ValueError here)except TypeError:breakggen()
print(next(g))
print(g.throw(ValueError))
print(next(g))
print(g.throw(TypeError))
输出结果为
normal value
we got ValueError here
normal value
normal value 2
Traceback (most recent call last):File h.py, line 15, in moduleprint(g.throw(TypeError))
StopIteration
解释
print(next(g))会输出normal value并停留在yield ‘normal value 2’之前。由于执行了g.throw(ValueError)所以会跳过所有后续的try语句也就是说yield ‘normal value 2’不会被执行然后进入到except语句打印出we got ValueError here。 然后再次进入到while语句部分消耗一个yield所以会输出normal value。print(next(g))会执行yield ‘normal value 2’语句并停留在执行完该语句后的位置。g.throw(TypeError)会跳出try语句从而print(‘here’)不会被执行然后执行break语句跳出while循环然后到达程序结尾所以跑出StopIteration异常。 下面给出一个综合例子用来把一个多维列表展开或者说扁平化多维列表)
def flatten(nested):try:#如果是字符串那么手动抛出TypeError。if isinstance(nested, str):raise TypeErrorfor sublist in nested:#yield flatten(sublist)for element in flatten(sublist):#yield elementprint(got:, element)except TypeError:#print(here)yield nestedL[aaadf,[1,2,3],2,4,[5,[6,[8,[9]],ddf],7]]
for num in flatten(L):print(num)
如果理解起来有点困难那么把print语句的注释打开在进行查看就比较明了了。
总结
按照鸭子模型理论生成器就是一种迭代器可以使用for进行迭代。第一次执行next(generator)时会执行完yield语句后程序进行挂起所有的参数和状态会进行保存。 再一次执行next(generator)时会从挂起的状态开始往后执行。 在遇到程序的结尾或者遇到StopIteration时循环结束。 可以通过generator.send(arg)来传入参数这是协程模型。 可以通过generator.throw(exception)来传入一个异常。throw语句会消耗掉一个yield。 可以通过generator.close()来手动关闭生成器。next()等价于send(None)