生成器迭代器
Posted 寻寻觅觅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了生成器迭代器相关的知识,希望对你有一定的参考价值。
一、列表生成式
现在有个需求,看列表[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
,要求你把列表里的每个值加1
a = [i+1 for i in range(10)]
print(a)
输出:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
这样的写法就叫做列表生成式。
二、生成器(generator)
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]
改成(),
就创建了一个generator:
g = (i+1 for i in range(10)) # 创建了一个generator
相当于generator保存了一种算法,当你需要时才给你生成数据,你不需要,它就停在那里,这与列表相比,比较节省内存空间。
generator只能前进,不能往后退,当生成完了之后就不再生产。
可以通过next()函数获得生成器的下一个值,若生产完了就会报错。
可以用for循环或while循环来迭代generator:
g = (i+1 for i in range(3)) # 创建了一个generator
for n in g:
print(n)
输出:
1
2
3
使用for循环,当生成完之后不会报错。
在python3中,range()就是一个生成器,执行语句:print(range(3)) ,结果是:range(0,3)
在python2中,执行语句:print(range(3)) ,结果是:0,1,2 ,相当于range()是一个列表
在python2中,执行语句:print(xrange(3)) ,结果是:range(0,3),与python3一样,是一个生成器
斐波拉契数列
斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89,..........
第0项是0,第1项是第一个1。这个数列从第2项开始,每一项都等于前两项之和。
有如下代码:
def fib(max):
n, a, b = 0, 0, 1 # n:循环次数,a:第一个数,b:第二个数
while n <= max:
print(b)
a, b = b, a+b
n += 1
return
fib(4)
输出:
1
1
2
3
5
程序将会输出所有计算结果。
使用生成器:
def fib(max):
n, a, b = 0, 0, 1 # n:循环次数,a:第一个数,b:第二个数
while n <= max:
yield b # 把函数的执行结果冻结在这一步,并且把b的值返回给外面的next(f)
a, b = b, a+b
n += 1
f = fib(4)
print(next(f)) # 调用才会生成
print(next(f))
输出:
1
1
这便是定义generator的另一种方法:如果函数定义中包含了yield关键字,那么这个函数就不再是一个普通的函数,而是一个生成器。
函数与生成器的区别:
它们的执行流程不同,函数是顺序执行,遇到return或者函数最后一行语句时结束;生成器是在每次调用next()的时候执行,遇到yield语句返回,再次next()调用时,从上次返回的yield语句处继续执行。
return的作用:返回并终止函数
yield的作用:返回并冻结当前的执行过程,(即停留在这一步)
next()的作用:唤醒冻结的执行过程,继续执行,直到遇到下一个yield
send()的作用:唤醒冻结的执行过程,继续执行,并发送一个消息到生成器内部
注意:在生成器中使用return,程序会报错。
def func(n):
count = 0
while count < n:
print(count)
count += 1
sign = yield count
if sign == "stop": # 当收到“stop”时停止生产
print("sign:", sign)
break
else:
print("continue:", sign)
res = func(5)
print(next(res))
res.send("start")
res.send("stop")
输出:
0
1
continue: start
1
sign: stop
三、迭代器
可以直接作用于for
循环的对象统称为可迭代对象:Iterable
包括以下几种数据类型:
一类是集合数据类型,如list
、tuple
、dict
、set
、str
等;
一类是generator
,包括生成器和带yield
的generator function。
可以使用isinstance()
判断一个对象是否是Iterable
对象:
from collections import Iterable
print(isinstance([], Iterable)) # 判断列表是否是可迭代对象
print(isinstance({}, Iterable)) # 判断字典是否是可迭代对象
print(isinstance("asdfg", Iterable)) # 判断字符串是否是可迭代对象
print(isinstance((x for x in range(10)), Iterable)) # 判断生成器是否是可迭代对象
print(isinstance(100, Iterable)) # 判断数字是否是可迭代对象
输出:
True
True
True
True
False
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:
from collections import Iterator
print(isinstance([], Iterator)) # 判断列表是否是迭代器
print(isinstance({}, Iterator)) # 判断字典是否是迭代器
print(isinstance("asdfg", Iterator)) # 判断字符串是否是迭代器
print(isinstance((x for x in range(10)), Iterator)) # 判断生成器是否是迭代器代对象
print(isinstance(100, Iterator)) # 判断数字是否是迭代器
输出:
False
False
False
True
False
生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。
把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数:
from collections import Iterator
print(isinstance(iter([]), Iterator)) # 将列表变为迭代器
输出:
True
你可能会问,为什么list
、dict
、str
等数据类型不是Iterator
?
这是因为Python的Iterator
对象表示的是一个数据流,Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
小结
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;
集合数据类型如list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。
Python3的for
循环本质上就是通过不断调用next()
函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
print(x)
实际上完全等价于:
it = iter([1, 2, 3, 4, 5]) # 获得一个Iterator对象
while True: # 循环
try:
x = next(it) # 获得下一个值
print(x)
except StopIteration:
break # 遇到StopIteration就退出循环
以上是关于生成器迭代器的主要内容,如果未能解决你的问题,请参考以下文章
python:可迭代对象,迭代器,生成器函数,生成器的解析举例代码说明