iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Python:生成器
  • 641
分享到

Python:生成器

生成器Python 2023-01-31 02:01:18 641人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

    生成器是python中的一个高级用法,有段时间我对生成器的理解颇为费劲,直到我看到一句话“yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行”后,让我恍然大悟,这是生成器中的状态挂起,这句话让我想起

    生成器是python中的一个高级用法,有段时间我对生成器的理解颇为费劲,直到我看到一句话“yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行”后,让我恍然大悟,这是生成器中的状态挂起,这句话让我想起了在大学时玩ARM单片机时经常碰到的一个概念——中断,单片机在遇到中断信号时,处理中断程序前也要先保护现场,即系统要在执行中断程序之前,必须保存当前处理机程序状态字PSW和程序计数器PC等的值,待中断程序执行完成后在回复现场继续执行下面的程序。仔细想想,个人觉得在保护“现场”这一点上,两者中的道理还是差不多的(也许你并不这么认同),有时候一个新概念的理解就是卡在一个小知识点上,我之前一直不明白“生成器挂起状态”是什么东西,但是回头瞬间想起以前学过的知识,然后类比,有些东西也就恍然大悟了,也是这个“联想”让我对生成器有了更深刻的理解,使用起来也得心应手。现在工作当中,特别是在做数据统计时,碰到了特别长的列表时,我都是用生成器,不进可以节省内存,而且代码更加优雅。下面就来讲讲生成器,不正之处欢迎批评指正!

   生成器就是按照一定算法生产的序列,也就是序列元素可以按照某种算法推算出来,即在循环的过程中不断推算出后续的元素,这样就不必创建完整的序列,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。

(一)生成器语法

生成器表达式: 通列表解析语法,只不过把列表解析的[]换成()

生成器表达式能做的事情列表解析基本都能处理,只不过在需要处理的序列比较大时,列表解析比较费内存。

>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x104feab40>

L是一个list,而g是一个generator。如果要一个一个打印出来,可以通过generator的next()方法。每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。这里就不过多阐述,大家可以在终端试试,不断执行g.next(),同时可以用sys.getsizeof()来比较下L和g所用内存的大小,这里列表元素比较少,看不出生成器的优势,但是,对于g,把推到式中的range(10)改成range(100),range(100),g所占内存是不会改变的,大家可以试试。


生成器函数: 在函数中如果出现了yield关键字,那么该函数就不再是普通函数,而是生成器函数。

但是生成器函数可以生产一个无限的序列,这样列表根本没有办法进行处理。yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator。

def gensquares(N):
    for i in range(N):
        yield i ** 2 
        
for item in gensquares(5):
    print item

这是个简单的例子,使用生成器返回自然数的平方。


(二)生成器的方法

我们可以用dir()函数来看看生成器对象的方法,如下:

['__class__', '__delattr__', '__doc__', '__fORMat__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']


它里面有__iter__()和next()方法,这不就是迭代器协议要满足的两个基本条件吗?(不了解迭代器协议,可以看之前的博文,点此)也就是说生成器是一个特殊的迭代器。

close()

手动关闭生成器函数,后面的调用会直接返回StopIteration异常。看下面简单例子:

wKiom1hLkvjBvTB5AABZKcZX7Ls839.png-wh_50

    

send()

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

这是生成器函数最难理解的地方,也是最重要的地方。

首先看个简单的例子

#coding=utf-8
def fun(value=None):
    print "begin"

    while 1:
        try:
            value = (yield value)
            print "yield"
        except Exception,e:
            value = e


g = fun(8)
print g.next()
print "==============="
print g.next()
print "==============="
print g.next()

运行结果如下:

wKioL1hLroeTDTlYAAAw15CTFsM013.png-wh_50

    由上图的运行结果可知,生成器函数调用后,它的函数体并没有执行,而是到第一次调用next()时才开始执行,而且是执行到yield表达式为止,此时就要状态挂起,第二次调用next()时再恢复之前的挂起状态接着执行,所以第一次执行next()时,并没有打印出"yield",到第二次调用next()时,第一个执行的就是print "yield"语句,所以也就打印出了"yield",直到再次遇到yield表达式,然后再挂起,依次类推。

这里还要提到一点就是yield表达式,第一次调用next()时,value = yield v语句中只执行了yield v这个表达式,而赋值操作并未执行。只有第二次调用next()时yield表达式的值赋给了value,而yield表达式的默认“返回值”是None.

这一块大家可以参考这篇博文

在函数里单独的yield 5 与m = yield 5还是有区别的。

这可能有点难理解,举个例子来验证下:

#coding=utf-8
class A(object):
    def __init__(self,v):
        self._value = v
    def fun(self,value):
        print "begin"
        while 1:
            try:
                self._value = (yield value)
                print "aaa",self._value
                print "yield"
            except Exception,e:
                self._value = e

G = A(8)
g = G.fun(88)
print "_value  " ,  G._value
print g.next()
print "_value  " ,G._value
print "==============="
print g.next()
print "_value  " ,G._value
print "==============="
print g.next()
print "_value  " ,G._value

运行结果如下:

wKiom1hLtdnzXFJVAABIR1AiII8552.png-wh_50

从运行结果上来看,第一次调用next()时,G._value的值并没有改变,说明此时self._value = (yield value)并没有执行赋值操作,第二次调用next()时,G._value的值改变了,为None,说明执行了赋值操作。


有了上面的一些基础,理解send()方法应该很容易,看下面例子:

#coding=utf-8
def fun(v):
    while 1:

        value = (yield v)
        if value == 14:
            break
        v = 'get: %s' % value

g = fun(None)
print g.send(None)
print g.send(10)
print g.send(12)
print g.send(14)


执行流程:

1.通过g.send(None)或者next(g)可以启动生成器函数,并执行到第一个yield语句结束的位置。

此时,执行完了yield语句,但是没有给value赋值。注意:在启动生成器函数时只能send(None),如果试图输入其它的值都会得到错误提示信息。这里,如果你去掉g.send(None)这句,就会报错。

2.通过g.send(10),会传入10,并赋值给value,然后计算出v的值,并回到while头部,执行yield v语句有停止。此时会输出"get: 10",然后挂起。

3.通过g.send(12),会重复第2步,最后输出结果为"Got:12"

4.当我们g.send(14)时,程序会执行break然后推出循环,最后整个函数执行完毕,所以会是StopIteration异常。

wKiom1hLwzTTePp4AABPlYMLy1A260.png-wh_50



其实,send()是全功能版本的next(),next()相当于send(None),前面提到过yield表达式有“返回值”,send()作用就是控制这个“返回值”的,使得yield表达式的返回值是它的实参。

这一句要好好理解,看上面的例子,最后打印出来的值都是函数中v的值(也就是实参)。

throw()

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

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

#coding=utf-8
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)

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异常。

最后运行结果如下:

wKiom1hLxxOjhC2SAABWgtuh7gQ091.png-wh_50


生成器的主要三个方法中,send()方法是比较难理解的,不过只要记住send()作用就是控制yield表达式“返回值”的,使得yield表达式的返回值是它的实参。

最后总结起来就这么几句:

1.生成器就是一种迭代器,可以使用for进行迭代。

2.第一次执行next(generator)时,会执行完yield语句后程序进行挂起,所有的参数和状态会进行保存。再一次执行next(generator)时,会从挂起的状态开始往后执行。在遇到程序的结尾或者遇到StopIteration时,循环结束。

3.生成器函数和常规函数几乎是一样的。它们都是使用def语句进行定义,差别在于,生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值

4.可以通过generator.send(arg)来传入参数,这是协程模型。

5.可以通过generator.throw(exception)来传入一个异常。throw语句会消耗掉一个yield。

6.可以通过generator.close()来手动关闭生成器。

7.next()等价于send(None)



--结束END--

本文标题: Python:生成器

本文链接: https://www.lsjlt.com/news/185504.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • python列表生成器与生成器
    列表生成器是python内置的非常简单却非常强大的。生成一个列表[1,2,3,4]可以用list(range(1,5))列表生成器的应用如果上面的公式加上判断,就可以筛选出想要的结果,比如仅需要偶数的平方使用多层循环生成器生成器与列表生成器...
    99+
    2023-01-31
    生成器 列表 python
  • python生成器
    所属网站分类: python基础 > 装饰器,迭代器,生成器 作者:慧雅 原文链接: http://www.pythonheidong.com/blog/article/20/ 来源:python黑洞网 www.python...
    99+
    2023-01-31
    生成器 python
  • python 生成器
    先来讲一个列表生成式列表生成式:就是一个用来生成列表的特定语法形式的表达式。基础语法格式[exp for iter_var in iterable]普通创建列表是这样的a = [1...
    99+
    2023-01-30
    生成器 python
  • Python---生成器
    # 生成器 # 通过列表生成式,我们可以直接创建一个列表 # 但是,受到内存限制,列表容量肯定是有限的 # 创建一个100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了 #...
    99+
    2023-01-31
    生成器 Python
  • Python:生成器
        生成器是Python中的一个高级用法,有段时间我对生成器的理解颇为费劲,直到我看到一句话“yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行”后,让我恍然大悟,这是生成器中的状态挂起,这句话让我想起...
    99+
    2023-01-31
    生成器 Python
  • Python生成器/生成器函数/推导式/
    1. 生成器   生成器的本质就是迭代器   在python中有三种⽅方式来获取⽣生成器:     1. 通过生成器函数     2. 通过各种推导式来实现⽣成器     3. 通过数据的转换也可以获取生成...
    99+
    2023-01-31
    生成器 函数 Python
  • Python 生成器 generator
    前提条件: 如果函数含有yield就是生成器,执行过程中遇到yield就跳出。 举例:def gen():for i in range(10):x = yield iprint(x)g=ge()print(g.send(None)))pri...
    99+
    2023-01-31
    生成器 Python generator
  • Python生成器和基于生成器的协程
    小编给大家分享一下Python生成器和基于生成器的协程,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一、什么是生成器Generator生成器就是可以生成值的函数2.当一个函数里有了 yield关键字就成了生成器3.生成器可...
    99+
    2023-06-15
  • python 生成器&迭代器
      一、生成器 1、列表生成器:列表生成式就像是一个厨师,他只会做这n(n为任意整数)道菜,想吃甚麽做甚麽,不吃不做,不浪费空间;而列表表达式就相当于已经做好的n盘菜,占用空间。2、生成器的创建方法: s = (x*2 for x in...
    99+
    2023-01-30
    生成器 迭代 python
  • Python之yield生成器
    1、对比range和xrange的区别:>>> print range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> print xrange(10) xrange(10)...
    99+
    2023-01-31
    生成器 Python yield
  • 【25】Python生成器generat
    列表生成式一个小题目:将里列表[0,1,2,3]里面的数值都加1.方法1: a=[0,1,2,3] b=[] for i in range(len(a)): b.append(i+1) a=b print(a) 方法2: a = [...
    99+
    2023-01-31
    生成器 Python generat
  • python -- 密码生成器
    小Q:中庭地白树栖鸦,冷露无声湿桂花;今夜月明人尽望,不知秋思落谁家? 王建《十五夜望月》两个密码生成代码实例=============================================================#!/u...
    99+
    2023-01-31
    生成器 密码 python
  • Python中的生成器
    目录1.列表生成式2.生成器1.列表生成式 代码演示: # 列表生成式 list_1 = [x**2 for x in range(10)] # x**2处也可以放函数 pri...
    99+
    2022-11-12
  • 详解Python生成器和基于生成器的协程
    目录一、什么是生成器二、基于生成器的协程三、协程的注意点四、协程装饰器五、python3原生协程一、什么是生成器 Generator 1.生成器就是可以生成值的函数 2.当一个函数里...
    99+
    2022-11-12
  • python迭代器和生成器
    1.经典迭代器 import re RE_WORD = re.compile('\w+') class Sentence: def __init__(self, text): self.text = te...
    99+
    2023-01-30
    生成器 迭代 python
  • Python迭代器与生成器
    生成器仅仅拥有生成某种东西的能力,如果不用__next__方法是获取不到值得。创建一个生成器函数>>> def scq(): ...    print("11") # 当函数代码块中遇到yield关键字的时候,这个函数就是...
    99+
    2023-01-31
    生成器 迭代 Python
  • python中的生成器(generator)
    一、生成器 生成器是 Python 中非常有用的一种数据类型,它可以让你在 Python 中更加高效地处理大量数据。生成器可以让你一次生成一个值,而不是一次生成一个序列,这样可以节省内存并提高性能 二、实现generator的两种方式 py...
    99+
    2023-09-18
    python 前端 linux
  • python进阶之生成器
    迭代器   什么叫迭代     可以被for循环的就说明他们是可迭代的,比如:字符串,列表,字典,元祖,们都可以for循环获取里面的数据   下面我们看一个代码:   1 number = 12345 2 for i in number...
    99+
    2023-01-30
    进阶 生成器 python
  • Python生成器的用法
    这篇文章主要讲解了“Python生成器的用法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python生成器的用法”吧!一、生成器在 Python 中,使用了 yield 的函数被称为生成器...
    99+
    2023-06-02
  • python高级-生成器(17)
    1. 什么是⽣成器 通过列表⽣成式,我们可以直接创建⼀个列表。但是,受到内存限制,列表容量肯定是有限的。⽽且,创建⼀个包含100万个元素的列表,不仅占⽤很⼤的存储空间,如果我们仅仅需要访问前⾯⼏个元素,那后⾯绝⼤多数元素占⽤的空间都⽩⽩浪...
    99+
    2023-01-31
    生成器 高级 python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作