生成器generator和迭代器Iterator
一、列表生成式
在学习生成器迭代器之前先了解一下什么是列表生成式,列表生成式是Python内置的非常简单却强大的可以用来创建list的生成式。什么意思?举个例子,如果想生成列表[0,1,2,3,4,5]可以使用list(range(6)),但是如果想要生成[,,,,,]即[0,1,4,9,16,25]怎么做?
#方法一:循环
>>> L = []
>>> for x in range(6):
... L.append(x**2)
...
>>> L
[0, 1, 4, 9, 16, 25]
#方法二:列表生成式
>>> [x**2 for x in range(6)]
[0, 1, 4, 9, 16, 25]
观察方法一、方法二可以发现,使用方法二列表生成式更为简洁、明了,方法二就是列表生成式。再来几个例子加深理解:
#计算正整数0-5之间偶数平方
>>> [x**2 for x in range(6) if x%2==0]
[0, 4, 16]
#列表生成式使用两个变量
>>> d = {'a':'1','b':'2','c':'3'}
>>> [k+'='+v for k,v in d.items()]
['a=1', 'b=2', 'c=3']
二、生成器
通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那是否可以在循环过程中不断推算后续元素呢?这样就不必创建完整的list,从而节省大量空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
1.如何创建生成器
方法一:创建生成器最简单的方法就是把一个列表生成式的 [] 改为 () ,就创建了一个生成器:
#列表生成式
>>> L = [x**2 for x in range(6)]
>>> L
[0, 1, 4, 9, 16, 25] #生成器
>>> g = (x**2 for x in range(6))
>>> g
<generator object <genexpr> at 0x00000173FFBFA1A8> 注:L与g的区别仅在于最外层的[]和(),L是一个列表,而g是一个生成器。
方法二:要把普通函数变为生成器(generator),只需把 print(b) 改为 yield b 就可以了。 如果一个函数中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个生成器(generator)。
2.如何获得生成器的每一个元素?
方法一:如果要一个一个打印出来,可以通过 next() 函数获得生成器的下一个返回值。生成器保存的是算法,每次调用next(),就计算g的下一个元素的值,直到计算到最后一个元素,没有更多元素时,抛出StopIteration错误。
#通过next()获得生成器的每一个元素
>>> g = (x**2 for x in range(6))
>>> g
<generator object <genexpr> at 0x00000173FFBFA1A8>
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
方法二:使用for循环
#通过for循环获得生成器的每一个值
>>> g = (x**2 for x in range(6))
>>> for i in g:
... print(i)
...
0
1
4
9
16
25
3.生成器实例
练习一:著名的斐波那契数列(Fibonacci),除第一个和第二个数外,任意一个数都可以由前两个数相加得到:1,1,2,3,5,8,13,21,34...用列表生成式写不出来,如何用函数实现?
#方法一:
>>> def fib(max):
... n,a,b = 0,0,1
... while n < max:
... print(b)
... a,b = b,a+b
... n += 1
... return 'done'
...
>>> fib(6)
1
1
2
3
5
8
'done' #注:a,b = b,a+b 相当于 t = (b,a+b) a = t[0] b = t[1]
#方法二:生成器
>>> def fib(max):
... n,a,b = 0,0,1
... while n < max:
... yield b
... a,b = b,a+b
... n += 1
... return 'done'
...
>>> f = fib(6)
>>> f
<generator object fib at 0x00000173FFBFA1A8>
>>> for i in f:
... print(i)
...
1
1
2
3
5
8
最难以理解的就是生成器与函数的执行流程不一样。函数是顺序执行,遇到 return 语句或者最后一行函数语句就返回。而变成生成器的函数,在每次调用 next() 的时候执行,遇到 yield 语句返回,再次执行时从上次返回的 yield 语句处继续执行。
#生成器函数每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行 >>> def odd():
... print('第一步')
... yield 100
... print('第二步')
... yield 200
... print('第三步')
... yield 300
...
>>> g = odd()
>>> next(g)
第一步
100
>>> next(g)
第二步
200
>>> next(g)
第三步
300
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> #odd()生成器函数,在执行过程中遇到yield就中断,下次继续执行。执行3次yield后,已经没有yield可以执行,所以第4次调用报错。
生成器总结:
a、在Python中可以简单地把列表生成式改为generator,也可以通过函数实现复杂逻辑的generator。生成器的工作原理是在for循环过程中不断计算出下一个元素,并在适当条件结束for循环。对于函数改成的generator来说,遇到return语句或者执行到函数体最后一行语句,就是结束generator的指令,for循环随之结束。
b、普通函数和生成器函数区别:普通函数调用直接返回结果,而生成器函数的“调用”实际返回一个生成器对象
#普通函数
>>> r = abs(-1)
>>> r
1 #生成器函数
>>> g = fib(6)
>>> g
<generator object fib at 0x00000173FFBFA1A8>
三、迭代器
可以直接作用于 for 循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str 等;
一类是generator,包括生成器和带 yield 的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。可以使用isinstance()判断一个对象是否是Iterable对象:
#判断一个对象是否是可迭代对象Iterable
>>> from collections import Iterable
>>> isinstance([],Iterable)
True
>>> isinstance((),Iterable)
True
>>> isinstance({},Iterable)
True
>>> isinstance('aaa',Iterable)
True
>>> isinstance((x for x in range(5)),Iterable)
True
>>> isinstance(100,Iterable)
False
>>> isinstance(True,Iterable)
False
可以被 next() 函数调用并不断返回下一个值的对象称为迭代器:Iterator。可以使用isinstance()判断一个对象是否是Iterator对象:
#判断一个对象是否是迭代器:Iterator
>>> from collections import Iterator
>>> isinstance([],Iterator)
False
>>> isinstance((),Iterator)
False
>>> isinstance({},Iterator)
False
>>> isinstance('aaa',Iterator)
False
>>> isinstance((x for x in range(5)),Iterator)
True
可以发现生成器都是迭代器(Iterator)对象,但list、tuple、dict、str虽然是可迭代对象(Iterable),却不是迭代器(Iterator)。那有没有什么方法可以使list、tuple、dict、str等可迭代对象(Iterable)变成迭代器(Iterator)? iter() 函数可以做这件事!
#iter()将list、tuple、dict、set、str等可迭代对象变为迭代器
>>> isinstance(iter([]),Iterator)
True
>>> isinstance(iter(()),Iterator)
True
>>> isinstance(iter({}),Iterator)
True
>>> isinstance(iter('aaa'),Iterator)
True
为什么list、dict、str等数据类型不是Iterator?这是因为Python的迭代器(Iterator)对象表示的是一个数据流,迭代器(Iterator)对象可以被next() 函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
迭代器(Iterator)甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
迭代器总结:
a、凡是可作用于 for 循环的对象都是可迭代(Iterable) 类型;
b、凡是可作用于 next() 函数的对象都是 迭代器(Iterator) 类型,它们表示一个惰性计算的序列;
c、集合数据类型如list、dict、str等都是可迭代(Iterable)但不是迭代器(Iterator),不过可以通过iter()函数获得一个迭代器(Iterator)对象。
d、Python的for循环本质上就是通过不断调用next()函数实现的。
for x in [1, 2, 3, 4, 5]:
pass 实际上完全等价于: # 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break
四、迭代器生成器关系
1.对于可迭代对象,可以通过 iter() 函数获得迭代器对象,并且可以被next() 函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。
2.生成器是一种特殊的迭代器,生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果。return 与 yield 不同之处在于return返回后,函数会释放,而生成器不会。在直接调用next()方法或者用 for 语句进行下一次迭代时,生成器会从yield 下一句开始执行,直至遇到下一个 yield。
3.迭代器可以多次迭代,而生成器只能迭代一次。
#迭代器:可以多次迭代
>>> g = [x*x for x in range(5)]
>>> g
[0, 1, 4, 9, 16]
>>> for i in g:
... print(i)
...
0
1
4
9
16
>>> g
[0, 1, 4, 9, 16]
>>> for i in g:
... print(i)
...
0
1
4
9
16 #生成器:只能迭代一次
>>> f = (x*x for x in range(5))
>>> f
<generator object <genexpr> at 0x0000022BA065F0F8>
>>> for i in f:
... print(i)
...
0
1
4
9
16
>>> f
<generator object <genexpr> at 0x0000022BA065F0F8>
>>> for i in f:
... print(i)
...
>>>
生成器generator和迭代器Iterator的更多相关文章
- Python中生成器generator和迭代器Iterator的使用方法
一.生成器 1. 生成器的定义 把所需要值得计算方法储存起来,不会先直接生成数值,而是等到什么时候使用什么时候生成,每次生成一个,减少计算机占用内存空间 2. 生成器的创建方式 第一种只要把一个列表生 ...
- python生成器(generator)、迭代器(iterator)、可迭代对象(iterable)区别
三者联系 迭代器(iterator)是一个更抽象的概念,任何对象,如果它的类有next方法(next python3)和__iter__方法返回自己本身,即为迭代器 通常生成器是通过调用一个或多个yi ...
- Python之生成器(generator)和迭代器(Iterator)
generator 生成器generator:一边循环一边计算的机制. 生成器是一个特殊的程序,可以被用于控制循环的迭代行为.python中的生成器是迭代器的一种,使用yield返回值函数,每次调用y ...
- 学习python第十二天,函数4 生成器generator和迭代器Iterator
在Python中,这种一边循环一边计算的机制,称为生成器:generator 要创建一个generator,有很多种方法.第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个genera ...
- Python 生成器 Generator 和迭代器 Iterator
#最近一周刚开始接触python,基本的语法,和使用特性和Java差别还是蛮大的. 今天接触到Python的迭代器和生成器有点不是很明白,所以搜索了先关资料整理了一些自己的理解和心得 简述(Profi ...
- 【Python之路】特别篇--生成器(constructor)、迭代器(iterator)、可迭代对象(iterable)
生成器(constructor) 生成器函数在Python中与迭代器协议的概念联系在一起.包含yield语句的函数会被特地编译成生成器 !!! 当函数被调用时,他们返回一个生成器对象,这个对象支持迭代 ...
- 【python之路29】python生成器generator与迭代器
一.python生成器 python生成器原理: 只要函数中存在yield,则函数就变为生成器函数 #!usr/bin/env python # -*- coding:utf-8 -*- def xr ...
- Python的生成器Generator小结
一. 生成器的介绍 在介绍生成器(Generator)之前,我们首先需要熟悉列表生成式,列表生成式是Python内置的简单又强大的用来创建列表的生成式. 举个例子, 如果我们想生成[1*1,2*2,3 ...
- ES6中的迭代器(Iterator)和生成器(Generator)
前面的话 用循环语句迭代数据时,必须要初始化一个变量来记录每一次迭代在数据集合中的位置,而在许多编程语言中,已经开始通过程序化的方式用迭代器对象返回迭代过程中集合的每一个元素 迭代器的使用可以极大地简 ...
随机推荐
- 构建docker私有仓库+k8s-pod应用
环境版本系统:centos7.4docker-compose version 1.26.2docker-py version: 4.3.0CPython version: 2.7.5docker-ve ...
- 趣味vi:Do you love me?
看到网上有很多这样的小趣味exe,自己用labview也做了一个,可能有很多bug,马马虎虎能用,大家可以发给自己滴那个人,哈哈哈.源码vi和exe文件都在链接中https://files.cnblo ...
- P4609 建筑师
题目描述 小 Z 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 n 个建筑,每个建筑的高度是 1 到 n 之间的一个整数. 小 Z 有很严重的强迫症,他不喜欢有两个建筑的高度相同. ...
- 深入了解Kafka【四】消费者的Offset管理
1.Offset Topic Consumer通过提交Offset来记录当前消费的最后位置,以便于消费者发生崩溃或者有新的消费者加入消费者组,而引发的分区再均衡操作,每个消费者可能会分到不同的分区.我 ...
- servlet web项目连接数据库报错
报错信息类似这样: Wed May 27 14:15:54 CST 2020 WARN: Establishing SSL connection without server's identity v ...
- C004:要求用户输入一个美元数量,然后显示出增加5%税率后的相应金额
程序: #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { float amount; do{ printf(&q ...
- logback1.11的一个bug:有线程持续不断写入log文件,log文件就不会按设定以日期切换。
此Bug的解决方案请见:https://www.cnblogs.com/xiandedanteng/p/12205422.html logback是log4j的后继者,作者也是同一人,但其中的bug不 ...
- 虚拟机:主机能ping通虚拟机,虚拟机不可以ping通主机
解决办法: 1.虚拟机可使用nat模式 2.虚拟机使用桥接模式,并且和主机再同一个ip段内 3.主机的防火墙关闭(不推荐使用,主机不安全) 4.主机中有个ICMPv4-In这个规则需要启用: 打开防火 ...
- python中实现参数化的原理
k就是 <参数名>
- Web_php_unserialize 攻防世界
1.$var输入处有一个正则过滤 2._wakeup处需要过滤 编写脚本如下 后在网页输入?var=TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4Oi ...