[编程经验]Python生成器、迭代器与yield语句小结
今天要分享的内容是Python的生成器、迭代器与yield语句。主要包括什么是生成器,如何定义一个生成器,如何调用生成器包含的元素。迭代器也是一样的,最后介绍yield语句,以及它和生成器有什么关系,这是本文的重点。
[* ! *] 理解本文需要一定的基础,需要了解Python列表的定义,基本操作,字典,元组,字符串的概念。Python中for循环的语法结构,以及需要知道
if __name__ =="__main__":
的作用是什么?
1. 迭代
首先来看一下迭代的定义:
如果给定一个列表list或元组tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。
用人话说一遍就是给一个列表或者元组,把里面的元素挨个看看都是啥,且只看一遍,就叫做迭代。下面写个简单的栗子。首先定义一个list_a,然后通过for循环遍历每一个元素,并打印出list_a中的每一个元素(图1),这就是对list_a迭代的过程。
迭代器的定义:
可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)。
一般来说迭代器都是可以迭代的。
图1
刚才介绍的是对Python中列表的迭代,那么对于其他对象是不是也可以迭代呢?怎么来判断一个对象是不是可迭代的呢?我们可以利用collections模块中的Iterable函数来判断一个对象是不是可迭代的。比如:
分别定义列表,元组,字符串,字典,整数5种数据类型a,b,c,d,e,然后分别判断是否可迭代,见图2。结果可以看到除了整数不可迭代外,其他4种数据类型都是可迭代的。
图2
2. 列表生成式
顾名思义,列表生成式就是用来自动创建一个列表表达式。使用列表生成式来创建列表的一个好处是可以简化代码,使代码美观,减少工作量。
举个简单的栗子,我们定义一个列表,name_list,然后利用列表生成式,将name_list中的大写字母全部变为小写字母。其中
[name.lower() for name in name_list]
就是列表生成式,首先定义对象的某种运算,然后定义一个for循环来遍历对象。
图3
3. 生成器
列表生成式一般用于列表不是特别长,占用内存比较小的情况,如果数据量很大,生成器是比列表生成式更好的选择。在Python中,一边进行某种运算,一遍进行循环的机制称为生成器(Generator)。定义一个生成器有一种很简单的方法,就是把列表生成式中的[ ]改为( )即可。还是刚才的栗子,我们把生成器对象打印一下,看到
<generator object <genexpr> at 0x00000000038FF828>
lower_name_list是一个generator object,生成器对象。
图4
然后介绍一下怎么查看这个生成器对象中有哪些元素?对于生成器对象来说,使用生成器的next()方法来输出每一个对象。Next()的意思就是下一个,就好像是next()对生成器说,来吧,下一个,生成器就把下一个元素吐出来了,知道生成器中没有可迭代的对象的时候,就会抛出StopIteration异常(图5)。
图5
接下来我们学习另外一种输出生成器中元素的方法,就是用for循环来迭代生成器中的元素(图6)。这是因为生成器是一种可迭代的对象,所以可以使用for循环来遍历。
>>> isinstance(lower_name_list1,Iterable)
>>> True
图6
4. yield语句
接下来就到了我们今天的重点要介绍的东西,这里必须强行安利一波,yield很有用很有用,最好能熟练掌握。然后为什么这么说呢?这个我要扯一会儿深度学习,我们说的机器学习,或者深度学习,其中非常重要的一个环节就是读取数据,就是把数据读进来然后交给我们的模型去训练,去学习。那么问题来了,给你一个100万张图片,或者1G的文本数据,你怎么来读数据?传统的方法在求解机器学习算法的时候,大部分是使用批梯度下降法(Batch Gradient Descent),一般是一次性把数据都读进来,然后整体做梯度下降。但是现在是大数据时代了,数据量一般都很大,如果一次读进来,内存肯定不够用,如果是GPU,同样显存也不够用。所以就有了后来的mini-batch 梯度下降,到底有多mini呢,通常远远小于全部数据量,这个数字就是我们在训练模型的时候取得batchsize的大小。意思就是从一个很大的数据集里面,每次只取很小的一部分数据集,然后遍历整个数据集。
这个思想和Python的yield语句极为吻合,所以我强烈推荐大家掌握yield语句。下面我们开始yield语句的学习。
首先来看一下Python官方文档中,对yield的解释。
The yield statement is only used when defining a generator function, and is only used in the body of the generator function. Using a yield statement in a functiondefinition is sufficient to cause that definition to create a generatorfunction instead of a normal function.
意思是:yiled语句仅在定义一个生成器函数的时候使用,并且在生成器函数的函数体里面使用。在函数定义中使用yield语句之后,这个函数就不是一般的函数,而是生成器函数。
When a generator function is called, it returns an iterator known as a generator iterator, or more commonly, a generator. The body of the generator function is executed by calling the generator’s next() method repeatedly until it raises an exception.
当我们调用生成器函数的时候,将会返回一个生成器。我们通过调用生成器的next()方法来执行生成器函数,直到抛出异常。
When a yield statement is executed, the state of the generator is frozen and the value of expression_list is returned to next()‘s caller. By “frozen” we mean that alllocal state is retained, including the current bindings of local variables, the instruction pointer, and the internal evaluation stack: enough information is saved so that the next time next() is invoked, the function can proceed exactly as if the yield statement were just another external call.
当执行yield语句的时候,生成器对象是被冻结的,执行的结果只有next()方法所返回的list。冻结的意思是除了next()方法可以返回一个列表以外,其他的变量都不会执行。
把这段文档简单理解一下就是我们可以通过定义一个包含yield语句的函数,来定义一个生成器函数。这个生成器函数可以通过next()方法来执行。
下面我们举个具体的栗子,来看一下yield的执行原理。
首先我们定义了两个函数,yield_test()和yield_test2(),第一个函数是用return来返回输出值,第二个函数用yield来返回。这样做是为了反映return和yield的区别,也是为了体现包含yield语句函数的不同之处。为什么要做这个比较呢,说白了,yield语句其实也是返回一个值,只不过这个返回方式不太寻常,它是以生成器函数的形式返回,所以我们对比一下和return的区别,看看哪里不一样。
# - * -coding:utf-8 - * -
defyield_test():
list_a = range(5)
list_b = []
for i in list_a:
list_b.append(i * i)
return list_b
defyield_test2():
list_a = range(5)
for i in list_a:
yield i * i
if __name__ =="__main__":
results = yield_test()
results2 = yield_test2()
print("The results is:", results)
print("The type of results is: ",type(results))
print("The results2 is: ",results2)
print("The type of results2 is:", type(results2))
result_list = []
for x in results2:
result_list.append(x)
print("The result_list is:",result_list)
下面是程序输出结果
"""
('The results is:', [0, 1, 4, 9, 16])
('The type of results is: ', <type 'list'>)
('The results2is: ', <generator object yield_test2 at 0x00000000040F6630>)
('The type of results2 is: ', <type 'generator'>)
('The result_list is:', [0, 1, 4, 9, 16])
"""
今天的内容比较多,这些东西我大概花了很久,断断续续的才学会了(恩,其实我特别笨,但是我相信勤能补拙)。今天这个是比较全面的一个介绍,我大概花了4个小时写完,希望能通过今天的分享,给大家带来一点启发!
参考文献
[1] https://docs.python.org/2/reference/simple_stmts.html#the-yield-statement
[2] http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000
本文为作者原创,如有雷同,必然是别人抄我的。
- JDBC【介绍JDBC、使用JDBC连接数据库、简单的工具类】
- 019 单例模式的5种写法
- 图书管理系统【部署开发环境、解决分类、图书、前台页面模块】
- 图书管理系统【用户、购买、订单模块、添加权限】
- 04-02.总结switch,for,while,do。while跳转语句
- 图书管理系统【总结】
- JDBC【PreparedStatment、批处理、处理二进制、自动主键、调用存储过程、函数】
- JDBC【事务、元数据、改造JDBC工具类】
- JDBC【数据库连接池、DbUtils框架、分页】
- HTTP常见面试题
- Java基础-18(01)总结Map,HashMap,HashMap与Hashtable区别,Collections工具类
- 一个oracle查询引起的bug (r4笔记第59天)
- Java基础-18(02)总结Map,HashMap,HashMap与Hashtable区别,Collections工具类
- 特殊的物化视图刷新 (r4笔记第77天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 牛客2019跨年AK场题解(一)
- qiankun proxySand 沙箱
- MATLAB 与 C 语言的混合编程实战之辛普森积分法、自适应辛普森积分
- Java那些事之JDK环境配置及集成开发环境Eclipse安装
- 敲代码、作诗、写论文无所不能?史上最大AI模型GPT-3霸榜Github
- 数据结构实验——校园导游 实现最小生成树+最短路
- Salesforce LWC学习(二十六) 简单知识总结篇三
- 【Java】13 异常
- 【Java】14 多线程
- 【Java】16 字节流
- 【Java】17 字符流
- 极坐标系在数据可视化中的巧妙运用
- 【Java】18 增强流
- 【Java】19 网络编程
- 【Java】20 基于 TCP 协议的网络编程