上节内容说到Python的for语句循环本质上就是通过调用Iterable可迭代对象的__iter()__方法获得一个Iterator迭代器对象,然后不断调用Iterator迭代器对象__next()__方法实现的。Iterator迭代器对象则是一个需要实现__iter__()__next__()两个迭代器协议方法的对象。python中生成器提供了一种方便的方法来实现迭代器协议,而不需要必须实现__iter__()__next__()两个迭代器协议方法。

生成器的定义方式有两种,一种是调用生成器函数,一种是使用生成器表达式语法。

调用生成器函数

生成器函数是指在函数体中使用yield表达式仅返回结果的函数。yield表达式仅在定义生成器函数时使用,因此只能用在函数定义的主体中。在函数体中使用yield表达式会使该函数成为生成器函数。当生成器函数被调用时,它返回一个称为生成器的迭代器,该迭代器由python自动生成。然后,生成器控制了生成器函数的执行。因为返回的生成器是一个迭代器,所以生成器函数的执行结果也就可以被循环。当生成器的的__next__方法被调用时,生成器函数的函数体内的语句开始执行,执行进行到第一个yield表达式时,立即将yield表达式的结果返回给生成器的调用者,同时将生成器函数内部的状态挂起。即保持生成器函数的执行进度,和生成器函数内的局部状态:包括局部变量的当前绑定,指令指针,内部计算栈和任何异常处理的状态。当生成器的再次调用__next__方法来时,生成器函数恢复执行,并再次执行到yield表达式返回结果再保持状态,直到无法再执行到yield表达式。此时生成器自动抛出StopIteration异常。

我们先定义一个简单生成器函数,函数功能返回数字0-9的平方数

# 生成器函数,功能返回数字0-9的平方数
>>> def squares():
for i in range(10):
yield i**2 # 使用return关键字是普通函数,使用yield关键字函数变成了生成器函数

使用参数g接收调用生成器函数squares的结果,然后分别在shell查看squaresg这两个变量的类型

>>> g = squares()

#查看squares对象类型
>>> squares
<function squares at 0x035950C0> #查看g对象类型
>>> g
<generator object squares at 0x0358A930>

从上面可以看出变量squares是函数类型,变量ggenerator类型对象,generator从字面的理解上就是生成器类型。根据上一节迭代器中提到的知识,从collection模块引入Iterator的抽象基类,验证下generator类型是不是上面说的迭代器类型。

>>> from collections import Iterator
>>> isinstance(g,Iterator)
True

验证成功,说明生成器函数的执行结果确实是生成器,一种特殊的迭代器。

>>> for i in g:
print (i) 0
1
4
9
16
25
36
49
64
81

生成器表达式

除了使用生成器函数可以得到生成器,还可以生成器表达式得到生成器表达式。生成器表达式本身看起来像列表推到, 但不是用方括号而是用圆括号包围起来:

>>> g2 = (x**2 for x in range(10))
>>> g2
<generator object <genexpr> at 0x0359AFC0> >>> t = (1,2,3,4,5)
>>> g3 = (x**2 for x in t)
>>> g3
<generator object <genexpr> at 0x007F6180>

验证:

>>> isinstance(g2,Iterator)
True >>> isinstance(g3,Iterator)
True

使用:

>>> for i in g2:
print(i) 0
1
4
9
16
25
36
49
64
81 >>> for i in g3:
print(i) 1
4
9
16
25

和普通迭代器相比,生成器不单简化了迭代器的定义,还在使用效率上有提升。因为生成器在循环时,生成器函数每次只会返回一个结果,然后保持内部状态,所以生成器占用的内存是很小的。以下两个测试结果,第一个直接抛出MemoryError异常,第二个只能正确计算出结果。

# 全部数据先加载在1个列表上面,内存占用高
>>> s1 = sum([i for i in range(100000000)])
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
s1 = sum([i for i in range(100000000)])
File "<pyshell#6>", line 1, in <listcomp>
s1 = sum([i for i in range(100000000)])
MemoryError # 数据几乎不占内存
>>> s2 = sum((i for i in range(100000000)))
>>> s2
4999999950000000

Python内置类型(6)——生成器的更多相关文章

  1. Python内置类型性能分析

    Python内置类型性能分析 timeit模块 timeit模块可以用来测试一小段Python代码的执行速度. class timeit.Timer(stmt='pass', setup='pass' ...

  2. Python 内置类型 dict, list,线程安全吗

    近段时间发现一个 Python 连接数据库的连接是线程不安全的,结果惹得我哪哪儿都怀疑变量的多线程是否安全的问题,今天终于找到了正确答案,那就是 Python 内置类型 dict,list ,tupl ...

  3. 为什么继承 Python 内置类型会出问题?!

    本文出自"Python为什么"系列,请查看全部文章 不久前,Python猫 给大家推荐了一本书<流畅的Python>(点击可跳转阅读),那篇文章有比较多的"溢 ...

  4. python内置类型详细解释

    文章编写借鉴于内置类型 - Python 3.7.3 文档,主要用于自己学习和记录 python主要内置类型包括数字.序列.映射.类.实例和异常 有些多项集类是可变的.它们用于添加.移除或重排其成员的 ...

  5. Python——内置类型

    Python定义了丰富的数据类型,包括: 数值型:int, float, complex 序列:(iterable) str, unicode, tuple, list, bytearray, buf ...

  6. 易被忽略的Python内置类型

    Python中的内置类型是我们开发中最常见的,很多人都能熟练的使用它们. 然而有一些内置类型确实不那么常见的,或者说往往会被我们忽略,所以这次的主题就是带领大家重新认识这些"不同寻常&quo ...

  7. Python内置类型——set

    Python中,内置类型set和frozenset用来表示集合,我们首先查看这两个类型支持的特殊对象,从而可以理解他们的特性. >>> dir(set) ['__and__', '_ ...

  8. 3、python内置类型(0529)

    python的内置对象类型以及支持的运算 python对象的相关术语 python程序中保存的所有数据都是围绕对象这个概念展开的 程序中存储的所有数据都是对象 每个对象都有一个身份.一个类型和一个值 ...

  9. Python内置类型(4)--数值

    Python有以下三种的数值类型: 整型(integers), 浮点型(floating point numbers), 以及 复数(complex numbers).此外,布尔是整数的子类型. 数值 ...

随机推荐

  1. 创建第一次C语言程序

    在这里我以VS2015为例,做演示.为什么要去演示怎样创建项目尼,因为我写第一个程序时,不知道该怎样用VS创建我的第一个应用程序. 第一步:打开VS环境如下 第二步:在开始出点击“新建项目”或在右上角 ...

  2. Quartz.Net进阶之一:初识Job作业和触发器

        前几天写了一篇有关Quartz.Net入门的文章,大家感觉不过瘾,想让我在写一些比较深入的文章.其实这个东西,我也是刚入门,我也想继续深入了解一下,所以就努力看了一些资料,然后自己再整理和翻译 ...

  3. 探索未知种族之osg类生物---呼吸分解之更新循环二

    _scene->updateSceneGraph(*_updateVisitor); 我们用了前面4节才刚刚算是完成对DatabasePager::DatabaseThread::run()函数 ...

  4. Sum of Even Numbers After Queries LT985

    We have an array A of integers, and an array queries of queries. For the i-th query val = queries[i] ...

  5. vc++ openssl 程序签名

    RSA一般有两种应用场景:   1.公钥加密.私钥解密:这是数据安全通信领域最常见情形:   2.私钥加验.公钥验签:这主要用于数字签名. 我们这里用到的是第二种情况: 这里是基于OpenSSL,首先 ...

  6. 转载-对js中new、prototype的理解

    说明:本篇文章是搜集了数家之言,综合的结果,应向数家致谢 说到prototype,就不得不先说下new的过程. 我们先看看这样一段代码: <script type="text/java ...

  7. 《Linux就该这么学》第二天课程

    秦时明月经典语录:很多人被命运安排,而我安排命运.——卫庄 今天介绍了VM 虚拟机的安装以及Linux系统的安装,还讲解了Linux内核 RPM:降低软件的安装难度 源代码+安装规则→将程序源代码与安 ...

  8. ABP框架系列之四十二:(Object-To-Object-Mapping-对象映射)

    Introduction It's a common to map a similar object to another object. It's also tedious and repeatin ...

  9. EntityFramWork(3 code First 约定)

      Code First 约定 借助 Code First,可通过使用 C# 或 Visual Basic .NET 类来描述模型.模型的基本形状可通过约定来检测.约定是规则集,用于在使用 Code ...

  10. cropper,图片剪辑上传工具的使用

    cropper工具是一个功能强,兼容性好的一个图片裁剪和上传工具 GitHub地址:https://github.com/kesixin/Head_Cut_PC <div class=" ...