python之迭代器和生成器

生成器

要将生成器,先讲一下列表生成式。

列表生成式

如果我们要创建一个有规律的列表,比如说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方法获取下一个数据,但是我们无法获取其长度,它的计算是惰性的,只有在需要时才会计算下一个数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值