1、为什么要有生成器

Python在数据科学领域可以说是很火,我想有一部分的功劳就是它的生成器了吧。

我们知道我们可以用列表储存数据,可是当我们的数据特别大的时候,列表中的数据都是放在内存中,受到内存限制,列表容量肯定是有限的,而且还会降低计算机的性能。

如果仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。但如果列表中元素是按照某种算法推算出来,那我们就可以在循环的过程中不断推算出后续的元素,这样就不必创建完整的列表数据,从而节省大量的空间。

换句话说,我又想要得到庞大的数据,又想让它占用空间少,这时生成器就派上用场了,它可以说是一个不怎么占计算机资源的一种方法。

2、创建生成器

(1)简单创建生成器

将一个列表推导式(也叫列表生成式) [] 改为 ()即可创建一个生成器。

# 1.用推导式定义一个列表
# 关于推导式请看以前的文章有讲解。
my_list = [x * x for x in range(10)] # 打印列表
print(my_list)
# 查看my_list类型,是一个列表
print(type(my_list))
"""
输出结果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<class 'list'>
""" # 2.创建一个生成器
my_gen = (x * x for x in range(10)) # 打印生成器,是一个生成器对象
print(my_gen)
# 查看my_gen对象类型,是生成器类型
print(type(my_gen))
"""
输出结果:
<generator object <genexpr> at 0x0000000002575148>
<class 'generator'>
"""

(2)生成器的使用

# 创建生成器
my_gen = (x * x for x in range(10)) # 1。方式一:遍历生成器,使用next方法
print(my_gen.__next__()) # 0
print(my_gen.__next__()) # 1
print(my_gen.__next__()) # 4
print(my_gen.__next__()) # 9
# 或者
print(next(my_gen)) # 16
print(next(my_gen)) # 没有数据了则会抛出异常StopIteration # 2.方式二:遍历生成器的内容
for i in my_gen:
print(1) # 3.方式三:遍历生成器的内容
while True:
try:
# 调用next函数,获取下一个字符
result = next(my_gen)
print(result)
except StopIteration:
# 释放对it的引用,即废弃迭代器对象
del my_gen
# 不推出循环会成为私循环
break

提示:

  • 在上边练习中,可以看到和迭代器的用法差不多,在这里说明一下生成器本身就是一个迭代器。如果有对迭代器不清楚的可以查看前面说明迭代器的文章。
  • 上面方式一不断调用next()方法回去元素,实在是太变态了,正确的方法是使用for循环。
  • generator保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

总结:

  • []推导出来的是迭代器(Iterables)。
  • ()推导出来的是生成器(Generators)。

3、yield关键词

(1)yield关键词说明

如果我们想定义一个自己的生成器函数怎么办?

Python有yield关键词。其作用和return的功能差不多,就是返回一个值给调用者,只不过有yield的函数返回值后,函数依然保持调用yield时的状态,当下次调用的时候,在原先的基础上继续执行代码,直到遇到下一个yield或者满足结束条件结束函数为止。

啥意思?啥意思?啥意思?

  • 你先把yield关键字直观的看做return关键字,它首先是return的功能,就是在函数或方法中返回某个值,返回之后程序就不再往下运行了。
  • yield相当于返回一个值给调用者,停止执行函数中的语句,并且记住这个返回的位置。下次迭代时(或者执行next方法的时候),代码从yield记录位置的下一条语句开始执行。
  • 带有yield的函数不再是一个普通函数,而是一个生成器generator
  • 调用一个生成器函数,返回的是一个迭代器对象。

示例:

# 定义一个生成器函数
def testYield():
yield 1
yield 2
yield 3 # 获得一个生成器对象
ty = testYield() """
调用过程:
next(ty)相当于ty.__next__()
掉调用一次next(ty)时
就会执行testYield()内的方法。
当执行的第一行, yield 1时,
返回当前yield的值1给调用者,停止向下执行,并记录函数中当前的执行位置。
也就是每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值。
程序执行结束 当下次再调用next(ty)的时候,
还是会执行testYield()内的方法,
只不过是从yield 1下面一句开始执行。
以此类推。
"""
print(next(ty)) # 1
print(next(ty)) # 2
print(next(ty)) # 3
print(next(ty)) # StopIteration

注意:每次调用 testYield()函数都会生成一个新的 generator 实例,各实例互不影响。

(2)send()方法说明

send()方法和next()方法一样,都能让生成器继续往下走一步(下次遇到yield停),但send()能传一个值,这个值作为yield表达式整体的结果。

def testYield():
yield 1
y = yield 2
if (y == 'hello'):
yield 9
yield 3 ty = testYield() print(ty.__next__()) # 1
print(next(ty)) # 2
"""
第三次执行,send方法会把"hello"传递进去
就是y = "hello"
换句话说,就是send可以强行修改上一个yield表达式值
程序会从第二个yield的下一行开始执行
执行到下一个yield停止,并记录位置,返回结果。
"""
print(ty.send("hello")) # 9
print(next(ty)) # 3
print(next(ty)) # StopIteration

注意:第一次执行要么next(ty)要么ty.send(None),不能使用ty.send('xxxxx'),否则会报错的。

4、使用yield实现斐波那契数列

"""
数学中有个著名的斐波那契数列(Fibonacci),
数列中第⼀个数0,第⼆个数1,其后的每⼀个数都可由前两个数相加得到:
如下:
0,    1,    1,   2,    3,    5,   8,    13,    21,   34,    ... 现在我们想要通过for...in...循环来遍历迭代斐波那契数列中的前n个数。
那么这个斐波那契数列我们就可以⽤生成来实现,
每次迭代都通过数学计算来⽣成下⼀个数。
"""
from collections.abc import Iterable, Iterator class FibGenerator(object):
"""
fib数列生成器
""" # 初始化方法
def __init__(self):
# 斐波拉契数列中的前两个数
self.num1 = 0
self.num2 = 1 # 用来记录迭代次数(计数器)
self.i = 0 def gen(self, count):
# 用来保存迭代的总次数
self.count = count # 判断是否迭代结束,如果没有到达迭代次数,则返回数据
# self.count 需要迭代的次数
# self.i已迭代次数
while self.i < self.count:
yield self.num2
# 计算num1, num2的值,方便下次迭代返回
# 这里运用的是序列的封包与解包,不会的可以看我以前的文章(元组)
self.num1, self.num2 = self.num2, self.num1 + self.num2 # 执行一次next方法,计数器+1
self.i = self.i + 1 # 创建一个对象
fibGen = FibGenerator() # 调用生成器函数得到一个生成器
fg = fibGen.gen(15) # fibIter对象是一个迭代器
print(isinstance(fg, Iterable)) # True
print(isinstance(fg, Iterator)) # True # next方法方式获取数据
# print(next(fg)) # 1
# print(next(fg)) # 1
# print(next(fg)) # 2
# print(next(fg)) # 3
# print(next(fg)) # 5
# print(next(fg)) # 8 # 遍历生成器,可执行
for li in fg:
print(li)

5、总结

  • 生成器generator就是迭代器iterator的一种,以更优雅的方式实现的iterator,而且完全可以像使用iterator一样使用generator
  • 当然除了定义,定义一个iterator,你需要分别实现__iter__()方法方法和__next__()方法。但generator只需要一个yield关键字就可以。
  • Python生成器主要目的就是为了让你的代码更省资源,更高效!

参考:

『无为则无心』Python基础 — 63、Python中的生成器的更多相关文章

  1. 『无为则无心』Python基础 — 3、搭建Python开发环境

    目录 1.Python开发环境介绍 2.Python解释器的分类 3.下载Python解释器 4.安装Python解释器 5.Python解释器验证 1.Python开发环境介绍 所谓"工欲 ...

  2. 『无为则无心』Python基础 — 4、Python代码常用调试工具

    目录 1.Python的交互模式 2.IDLE工具使用说明 3.Sublime3工具的安装与配置 (1)Sublime3的安装 (2)Sublime3的配置 4.使用Sublime编写并调试Pytho ...

  3. 『无为则无心』Python基础 — 6、Python的注释

    目录 1.注释的作用 2.注释的分类 单行注释 多行注释 3.注释的注意事项 4.什么时候需要使用注释 5.总结 提示:完成了前面的准备工作,之后的文章开始介绍Python的基本语法了. Python ...

  4. 『无为则无心』Python基础 — 7、Python的变量

    目录 1.变量的定义 2.Python变量说明 3.Python中定义变量 (1)定义语法 (2)标识符定义规则 (3)内置关键字 (4)标识符命名习惯 4.使用变量 1.变量的定义 程序中,数据都是 ...

  5. 『无为则无心』Python基础 — 10、Python字符串的格式化输出

    目录 1.什么是格式化输出 2.Python格式化输出的五种方式 方式一:字符串之间用+号拼接 方式二:print()函数可同时输出多个字符串 方式三:占位符方式 方式四:f格式化方式(推荐) 方式五 ...

  6. 『无为则无心』Python基础 — 12、Python运算符详细介绍

    目录 1.表达式介绍 2.运算符 (1)运算符的分类 (2)算数运算符 (3)赋值运算符 (4)复合赋值运算符 (5)比较运算符 3.逻辑运算符 拓展1:数字之间的逻辑运算 拓展2:Python中逻辑 ...

  7. 『无为则无心』Python基础 — 44、对文件和文件夹的操作

    目录 1.os模块介绍 2.查看os模块相关文档 3.os模块常用方法 (1)文件重命名 (2)删除文件 (3)创建文件夹 (4)删除文件夹 (5)获取当前目录 (6)改变默认目录 (7)获取目录列表 ...

  8. 『无为则无心』Python序列 — 24、Python序列的推导式

    目录 1.列表推导式 (1)快速体验 (2)带if的列表推导式 (3)多个for循环实现列表推导式 2.字典推导式 (1)创建一个字典 (2)将两个列表合并为一个字典 (3)提取字典中目标数据 3.集 ...

  9. 『无为则无心』Python函数 — 29、Python变量和参数传递

    目录 1.Python的变量 (1)Python变量不能独立存在 (2)变量是内存中数据的引用 (3)注意点 2.了解变量的引用 3.Python的参数传递(重点) (1)示例 (2)结论 (3)总结 ...

随机推荐

  1. 基础概念(2):怎么用cc来编译?

    怎么用cc来编译? 总结卡片: cc的使用可以很简单,指定要转换的程序文件就可以了,比如:cc hello.c. 按cc的规则(我这里是clang-llvm),程序文件以.c或.cpp为后缀. cc有 ...

  2. 使用Hot Chocolate和.NET 6构建GraphQL应用(1)——GraphQL及示例项目介绍

    系列导航 使用Hot Chocolate和.NET 6构建GraphQL应用文章索引 前言 这篇文章是这个系列的第一篇,我们会简单地讨论一下GraphQL,然后介绍一下这个系列将会使用的示例项目. 关 ...

  3. Cesium1.70-介绍CesiumOSM建筑新特性

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 我们很高兴宣布Cesium OSM建筑,一个覆盖整个世界的3D建 ...

  4. 通过CVE-2021-43297漏洞在Apache Dubbo<=2.7.13下实现RCE

    目录 0 前言 1 找源头 1.1 找到触发点 1.2 可用的gadget 1.3 向上推触发点 2 构造poc 2.1 开启HttpServer 2.2 hessian2序列化过程简述 3 poc ...

  5. thrift基础知识

    1. 架构图 Thrift 包含一个完整的堆栈结构用于构建客户端和服务器端.下图描绘了 Thrift 的整体架构. 图 1. 架构图 如图所示,图中黄色部分是用户实现的业务逻辑,褐色部分是根据 Thr ...

  6. 关于网页中鼠标动作 onfocus onblur focus()

    其中: onFocus事件就是当光标落在文本框中时发生的事件. onBlur事件是光标失去焦点时发生的事件. 例如: <textarea onfocus="if(hello') {va ...

  7. Shell 脚本循环遍历日志文件中的值进行求和并计算平均值,最大值和最小值

    本文为博主原创,转载请注明出处: 最近在进行压测,为了观察并定位服务性能的瓶颈,所以在代码中很多地方加了执行耗时的日志,但这种方式只能观察,却在压测的时候,不太能准确的把握代码中某些方法的性能,所以想 ...

  8. condition_variable中的和wait_once和notify_one及notify_all实例代码

    // ConsoleApplication6.cpp : 定义控制台应用程序的入口点. #include "stdafx.h" #include<thread> #in ...

  9. 「LOJ 6373」NOIP2017 普及组题目大融合

    NOIP2017 普及组题目大融合 每个读者需要有某个后缀的书,可以暴力map,复杂度\(o(9*nlog(n))\),也可以反串建trie树,复杂度\(o(9*n)\). 故可以求出需要的最少的RM ...

  10. Linux 查看运行中进程的 umask

    线上某台虚机因为故障重装了系统(基线 CentOS 6.9 内核 2.6.x),重新部署了应用.这个应用会生成一个文件,到NFS挂载目录. 而这个 NFS 挂载目录是一个 FTP 服务器的目录.另一台 ...