python学习笔记--理解生成器

在学习python的时候,刚开始接触生成器(generator)这个概念的时候,其实还是不太能理解,感觉并没有完全掌握,今天看到这篇文章的时候,感觉对这个概念真的是有了进一步的了解,感觉生成器和列表解析的关系似乎有点类似于range和xrange函数的关系一样,列表解析是将要处理得到的序列都先生成了,而生成器是要通过多次迭代才会生成整个序列,否则每次执行就只是生成其中一个;此外,函数中出现了yield关键字,该函数就是生成器函数了。

这篇文章转载自深入理解Python中的生成器,另外也可以看看廖雪峰老师的教程。

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

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

>>> 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-
  1. 生成器函数: 在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器函数。
    但是生成器函数可以生产一个无线的序列,这样列表根本没有办法进行处理。
    yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator。

下面为一个可以无穷生产奇数的生成器函数。

def odd():n=1while True:yield nn+=2
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 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=0while True:receive=yield valueif receive=='e':breakvalue = 'got: %s' % receiveg=gen()
print(g.send(None))     
print(g.send('aaa'))
print(g.send(3))
print(g.send('e'))

执行流程:

  1. 通过g.send(None)或者next(g)可以启动生成器函数,并执行到第一个yield语句结束的位置。
    此时,执行完了yield语句,但是没有给receive赋值。
    yield value会输出初始值0
    注意:在启动生成器函数时只能send(None),如果试图输入其它的值都会得到错误提示信息。
  2. 通过g.send(‘aaa’),会传入aaa,并赋值给receive,然后计算出value的值,并回到while头部,执行yield value语句有停止。
    此时yield value会输出”got: aaa”,然后挂起。
  3. 通过g.send(3),会重复第2步,最后输出结果为”got: 3”

  4. 当我们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:breakg=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

解释:

  1. print(next(g)):会输出normal value,并停留在yield ‘normal value 2’之前。
  2. 由于执行了g.throw(ValueError),所以会跳过所有后续的try语句,也就是说yield ‘normal value 2’不会被执行,然后进入到except语句,打印出we got ValueError here。
    然后再次进入到while语句部分,消耗一个yield,所以会输出normal value。
  3. print(next(g)),会执行yield ‘normal value 2’语句,并停留在执行完该语句后的位置。
  4. 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语句的注释打开在进行查看就比较明了了。

总结

  1. 按照鸭子模型理论,生成器就是一种迭代器,可以使用for进行迭代。
  2. 第一次执行next(generator)时,会执行完yield语句后程序进行挂起,所有的参数和状态会进行保存。 再一次执行next(generator)时,会从挂起的状态开始往后执行。
    在遇到程序的结尾或者遇到StopIteration时,循环结束。
  3. 可以通过generator.send(arg)来传入参数,这是协程模型。
  4. 可以通过generator.throw(exception)来传入一个异常。throw语句会消耗掉一个yield。 可以通过generator.close()来手动关闭生成器。
  5. next()等价于send(None)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/409143.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

再不努力我就老了

借用李宇春歌里的一句话&#xff0c;再不疯狂我们就老了。。。再不努力青春就没了。。。 今天在浏览校内时&#xff0c;发现右下角滚动的照片中&#xff0c;竟有一张是关于自己的&#xff0c;那是自己5年前的照片&#xff0c;满脸的稚嫩&#xff0c;连自己都承认那时的自己真的…

mysql主键外键_MySQL主键和外键使用及说明

摘自网上一个经典的例子&#xff1a;大哥和小弟一、外键约束MySQL通过外键约束来保证表与表之间的数据的完整性和准确性。外键的使用条件&#xff1a;1.两个表必须是InnoDB表&#xff0c;MyISAM表暂时不支持外键(据说以后的版本有可能支持&#xff0c;但至少目前不支持)&#x…

python学习笔记--迭代器

转载自理解Python的迭代器 首先&#xff0c;廖雪峰老师的教程中解释了迭代器和生成器&#xff0c;这篇文章只是补充和我个人的总结。 什么是迭代 可以直接作用于for循环的对象统称为可迭代对象(Iterable)。 可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterat…

【转载】周鸿祎:做产品体验先把自己切换到二傻子模式

我唯一能自吹的地方&#xff0c;就是本人在互联网里可能犯的错最多&#xff0c;挨的骂最多&#xff0c;然后也经历了很多失败&#xff0c;所以这样才有一些真实的感受。 建议大家把《定位》和《创新者的窘境》、《创新者的解答》这几本书放在身边反复读。你经历得越多&#xff…

mysql临时关闭索引功能_MYSQL中常用的强制性操作(例如强制索引)

mysql常用的hint对于经常使用oracle的朋友可能知道&#xff0c;oracle的hint功能种类很多&#xff0c;对于优化sql语句提供了很多方法。同样&#xff0c;在mysql里&#xff0c;也有类似的hint功能。下面介绍一些常用的。强制索引 FORCE INDEX复制代码代码如下:SELECT * FROM TA…

python学习--windows下安装Numpy包的错误:Unable to find vcvarsall.bat

今天在安装numpy包的时候&#xff0c;无论是通过pip install numpy 还是上网下载numpy包后安装都是出现问题&#xff1a;error: Unable to find vcvarsall.bat&#xff0c;于是百度了下&#xff0c;看到Windows下安装Python包(Numpy)的错误&#xff1a;Unabletofindvcvarsall.b…

使用多行sql字符串时,要注意不要忽略了空格

使用李刚老师编著的《疯狂Java讲义》&#xff08;第2版&#xff09;学习MySql数据库与JDBC编程&#xff08;使用Java 7 &#xff09;&#xff0c;其中第601页的ConnMySql.java 程序代码在Eclipse上运行&#xff0c;出现错误。 import java.sql.*; public class ConnMySql{publi…

Python学习笔记---理解zip()函数

转载自Python零碎知识(2):强大的zip 一、代码引导 首先看这一段代码&#xff1a; 1 >>> name(jack,beginman,sony,pcky)2 >>> age(2001,2003,2005,2000)3 >>> for a,n in zip(name,age):4 print a,n5 6 输出&#xff1a;7 jack 20018 beginma…

mysql8.0.11 安装顺序_mysql 8.0.11 安装步骤详解

本文为大家分享了mysql 8.0.11 安装步骤&#xff0c;供大家参考&#xff0c;具体内容如下第一步&#xff1a;下载安装包MYSQL官方下载地址&#xff1a;官方下载这里第一项是在线安装&#xff0c;第二项是离线包安装&#xff0c;我选择的是第二项(不用管你电脑是多少位的操作系统…

HDU1258 DFS

题意&#xff1a;求n个数中的某些数的和等于t&#xff0c;并输出 dfs 记录下已经输出过的&#xff0c;然后每次比较一下&#xff0c;这样就能避免重复 View Code 1 #include<stdio.h>2 #include<string.h>3 #include<stdlib.h>4 #include<algorithm>5…

Python 问题--encode、decode及shell中文输出

最近在使用python的时候&#xff0c;涉及到中文输出的时候经常会出现乱码的问题&#xff0c;所以就上网搜索了下&#xff0c;参考了1.Python字符串的encode与decode研究心得乱码问题解决方法&#xff1b; 2.python 中编码的再次学习 此外&#xff0c;更多详细字符编码的可以看…

MYSQL中什么是规范化_如何规范化SQL数据库

小编典典这是在脚本中标准化表的示例。我建议你做这样的事情e.g Table: tbl_tmpDataDate, ProductName, ProductCode, ProductType, MarketDescription, Units, Value2010-01-01, Arnotts Biscuits, 01, Biscuit, Store 1, 20, 20.002010-01-02, Arnotts Biscuits, 01, Biscuit…

python 模块学习--Numpy

Numpy是Python的一个科学计算库&#xff0c;提供了矩阵运算的功能。安装方法可以直接使用pip install numpy命令&#xff0c;也可以在http://sourceforge.net/projects/numpy/files/NumPy/上下载与python相应版本的exe文件。 这里就记录下在学习和使用Numpy中所用过的一些函数…

jsp 的 for循环

jsp 的 for循环jsp中同样可以使用for语句来循环输出内容。for与if一块使用会使得程序流程更加灵活。达到一些我们想要的效果例如&#xff1a;<% page language"java" contentType"text/html;" charsetutf-8"%><html><head><titl…

MySQl求奇数和函数_收集的48个Shell脚本小技巧

0. shell 调试复制代码 代码如下:sh -x somefile.sh在somefile.sh 文件里加上setx set-x1. 用 && || 简化if else复制代码 代码如下:gzip -t a.tar.gzif [[ 0 $? ]]; thenecho "good zip"elseecho "bad zip"fi可以简化为&#xff1a;复制代码 代…