生成器
要将生成器,先讲一下列表生成式。
列表生成式
如果我们要创建一个有规律的列表,比如说1-10:
# list1 = [1,2,3,4,5,6,7,8,9,10]
list1 = [i for i in range(1,11)] # 列表生成式
print(list1)
print(list1[2])
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
3
如果我们想要的是[1,4,9,16…]呢?我们也可以用列表生成式很方便的得到:
list1 = [x * x for x in range(1,11)]
甚至我们可以定义一个对应函数f,于是有:
list1 = [f(x) for x in range(1,11)]
生成器
我们可以发现,第一段代码中,用列表生成式生成了list1,并且可以用index来访问其中的值。那么我们将方括号改为圆括号试一下:
list1 = (x for x in range(1,11))
print(list1)
print(list1[2])
<generator object <genexpr> at 0x02F26470>
Traceback (most recent call last):
File "ListComprehensions.py", line 6, in <module>
print(list1[2])
TypeError: 'generator' object is not subscriptable
我们发现,list1不再是列表,而是一个生成器对象(generator object),并且不可以用下表来访问,那我们改怎么访问呢?
list1 = (x for x in range(1,11))
print(list1)
for i in list1:
print(i)
我们可以用for循环来获取每一个值。
为什么要使用生成器呢?通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generato
r)。
生成器中有一个__next__的方法,可以逐步获取下一个值:
list1 = (x for x in range(1,11))
print(list1)
print(list1.__next__())
print(list1.__next__())
print(list1.__next__())
print("break...")
print(list1.__next__())
print(list1.__next__())
print(list1.__next__())
根据上面代码,我们知道,生成器会记录之前的状态,即使我们中断了,再次调用它的next方法时,依旧会继续上次的运行。
注意生成器的两点:
1.只有next方法,只能往前走,不能后退;
2.只能一步步前进,不能跨大步,不能用index访问。
将函数改为生成器
想必大家都知道著名的斐波拉契数列了,我们来看下面的代码:
def fib(x):
n,a,b = 0,1,1
while n < x:
print(a)
a, b = b, a + b
n += 1
上面的代码可以输出指定长度的斐波拉契数列,它离生成器只有一步之遥,下面我们就将其改为生成器。
def fib(x):
n,a,b = 0,1,1
while n < x:
# print(a)
yield a
a, b = b, a + b
n += 1
g = fib(10)
print(g)
for x in g:
print(x)
上面我们同样可以输出指定长度的数列。生成器的结束条件有三种:
1.遇到yield关键字
2.遇到return
3.函数执行完
生成器还可以在单线程中实现并发,即所谓的协程,如下(参考alex):
import time
def consumer(name):
print("%s prepare to eat cake" % name)
while True:
cake = yield
print("cake [%s] is ate by [%s]" % (cake,name))
def producer(name):
c = consumer("rose")
c.__next__()
print("[%s] prepare to make cake..." % name)
for i in range(1,11):
time.sleep(1)
print("[%s] has made a cake[%s]" % (name,i))
c.send(i)
producer("jack")
迭代器
可迭代对象
可迭代对象的定义:
1.可以直接作用于for循环的数据类型,如list,str,tuple…
2.生成器,包括带yield的生成器函数。
这些可直接作用于for循环的对象统称为可迭代对象(iterable)
我们可以用isinstance来判断是否是可迭代对象(collections快废弃了)。
from collections import Iterable
print(isinstance([],Iterable)) # True
print(isinstance("abc",Iterable)) # True
print(isinstance(10,Iterable)) # False
print(isinstance((x for x in range(10)),Iterable)) # True
迭代器
迭代器的定义:可以被next()函数调用并不断返回下一个值的对象称为迭代器。
注意:
1.生成器一定是迭代器
2.迭代器一定是可迭代对象
3.可迭代对象不一定是迭代器(可以作用于for循环但是没有next方法)
from collections import Iterator
print(isinstance([],Iterator)) # False
print(isinstance("abc",Iterator)) # False
print(isinstance(10,Iterator)) # False
print(isinstance((x for x in range(10)),Iterator)) # True
将可迭代对象变为迭代器
我们可以使用iter方法将可迭代对象变为迭代器:
from collections import Iterator
print(isinstance(iter([]),Iterator)) # True
print(isinstance(iter("abc"),Iterator)) # True
iterator对象表示一个数据流,可以不断的用next方法获取下一个数据,但是我们无法获取其长度,它的计算是惰性的,只有在需要时才会计算下一个数据。