理解python中生成器用法


Posted in Python onDecember 20, 2017

生成器(generator)概念

生成器不会把结果保存在一个系列中,而是保存生成器的状态,在每次进行迭代时返回一个值,直到遇到StopIteration异常结束。

生成器语法

生成器表达式: 通列表解析语法,只不过把列表解析的[]换成()
生成器表达式能做的事情列表解析基本都能处理,只不过在需要处理的序列比较大时,列表解析比较费内存。

>>> gen = (x**2 for x in range(5))
>>> gen
<generator object <genexpr> at 0x0000000002FB7B40>
>>> for 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():
n=1
while
True:
yield
n
n+=2
odd_num
=
odd()
count
=
0
for
o
in
odd_num:
if
count
>=5:
break
print(o)
count
+=1

当然通过手动编写迭代器可以实现类似的效果,只不过生成器更加直观易懂

class Iter:
  def __init__(self):
    self.start=-1
  def __iter__(self):
    return self
  def __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
Iterable
>>>
from
collections
import
Iterator
>>>
isinstance(odd_num,
Iterable)
True
>>>
isinstance(odd_num,
Iterator)
True
>>>
iter(odd_num)
is
odd_num
True
>>>
help(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
...
>>> g=g1()
>>> next(g)  #第一次调用next(g)时,会在执行完yield语句后挂起,所以此时程序并没有执行结束。
1
>>> next(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'
...
>>>
g=g2()
>>>
next(g)  #程序停留在执行完yield
 'a'语句后的位置。
'a'
>>>
next(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'
...
>>> g=g3()
>>> next(g)
'hello'
>>> next(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
...
>>> g=g4()
>>> next(g)
1
>>> g.close()
>>> next(g)  #关闭后,yield 2和yield 3语句将不再起作用
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
StopIteration

send()

生成器函数最大的特点是可以接受外部传入的一个变量,并根据变量内容计算结果后返回。

这是生成器函数最难理解的地方,也是最重要的地方,实现后面我会讲到的协程就全靠它了。

def
gen():
  value=0
  while
True:
    receive=yield
value
    if
receive=='e':
      break
    value
=
'got: %s'
%
receive
g=gen()
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 <module>
 print(g.send('e'))
StopIteration

throw()

用来向生成器函数送入一个异常,可以结束系统定义的异常,或者自定义的异常。

throw()后直接跑出异常并结束程序,或者消耗掉一个yield,或者在没有下一个yield的时候直接进行到程序的结尾。

def
gen():
  while
True:
    try:
      yield
'normal value'
      yield
'normal value 2'
      print('here')
    except
ValueError:
      print('we
 got ValueError here')
    except
TypeError:
      break
g=gen()
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 <module>
  print(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
TypeError
    for
sublist
in
nested:
      #yield
 flatten(sublist)
      for
element
in
flatten(sublist):
        #yield
 element
        print('got:',
element)
  except
TypeError:
    #print('here')
    yield
nested
L=['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)

Python 相关文章推荐
Python 爬虫爬取指定博客的所有文章
Feb 17 Python
快速了解Python相对导入
Jan 12 Python
解决Python pandas df 写入excel 出现的问题
Jul 04 Python
使用pandas批量处理矢量化字符串的实例讲解
Jul 10 Python
详解利用Python scipy.signal.filtfilt() 实现信号滤波
Jun 05 Python
python opencv 图像拼接的实现方法
Jun 27 Python
django rest framework vue 实现用户登录详解
Jul 29 Python
pytorch索引查找 index_select的例子
Aug 18 Python
利用pytorch实现对CIFAR-10数据集的分类
Jan 14 Python
python可视化text()函数使用详解
Feb 11 Python
python 牛顿法实现逻辑回归(Logistic Regression)
Oct 15 Python
sublime3之内网安装python插件Anaconda的流程
Nov 10 Python
Python利用turtle库绘制彩虹代码示例
Dec 20 #Python
浅谈Python中range和xrange的区别
Dec 20 #Python
python机器学习实战之树回归详解
Dec 20 #Python
使用python 和 lint 删除项目无用资源的方法
Dec 20 #Python
python机器学习实战之K均值聚类
Dec 20 #Python
Python绘制3d螺旋曲线图实例代码
Dec 20 #Python
python机器学习实战之最近邻kNN分类器
Dec 20 #Python
You might like
SONY ICF-SW7600的电路分析
2021/03/02 无线电
PHP echo,print,printf,sprintf函数之间的区别与用法详解
2013/11/27 PHP
利用浏览器的Javascript控制台调试PHP程序
2014/01/08 PHP
php教程之魔术方法的使用示例(php魔术函数)
2014/02/12 PHP
PHP生成各种随机验证码的方法总结【附demo源码】
2017/06/05 PHP
JS创建优美的页面滑动块效果 - Glider.js
2007/09/27 Javascript
JQuery 引发两次$(document.ready)事件
2010/01/15 Javascript
javascript innerText和innerHtml应用
2010/01/28 Javascript
推荐40款强大的 jQuery 导航插件和教程(上篇)
2012/09/14 Javascript
基于Unit PNG Fix.js有时候在ie6下不正常的解决办法
2013/06/26 Javascript
用jquery存取照片的具体实现方法
2013/06/30 Javascript
JavaScript将XML转成JSON的方法
2015/03/12 Javascript
浅谈JavaScript中小数和大整数的精度丢失
2016/05/31 Javascript
nodejs个人博客开发第三步 载入页面
2017/04/12 NodeJs
Angular2环境搭建具体操作步骤(推荐)
2017/08/04 Javascript
详解A标签中href=&quot;&quot;的几种用法
2017/08/20 Javascript
js制作简单的音乐播放器的示例代码
2017/08/28 Javascript
详解@angular/cli 改变默认启动端口两种方式
2018/11/29 Javascript
通过vue写一个瀑布流插件代码实例
2019/09/07 Javascript
[01:11]辉夜杯战队访谈宣传片—CDEC.Y
2015/12/26 DOTA
[02:18]《我与DAC》之工作人员:为了热爱DOTA2的玩家们
2018/03/28 DOTA
[10:21]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Aster 选手采访
2021/03/11 DOTA
Python爬虫实例爬取网站搞笑段子
2017/11/08 Python
在Python中分别打印列表中的每一个元素方法
2018/11/07 Python
PyCharm安装Markdown插件的两种方法
2019/06/24 Python
Django继承自带user表并重写的例子
2019/11/18 Python
Python+OpenCV+图片旋转并用原底色填充新四角的例子
2019/12/12 Python
Python进行统计建模
2020/08/10 Python
matplotlib自定义鼠标光标坐标格式的实现
2021/01/08 Python
突袭HTML5之Javascript API扩展5—其他扩展(应用缓存/服务端消息/桌面通知)
2013/01/31 HTML / CSS
英国舒适型鞋履品牌:FitFlop
2017/05/17 全球购物
信息与计算科学专业推荐信
2014/02/23 职场文书
英语感谢信范文
2015/01/20 职场文书
邀请函格式范文
2015/02/02 职场文书
计算机专业自荐信范文
2015/03/26 职场文书
团组织关系介绍信
2019/06/24 职场文书