PYTHON迭代器和生成器

迭代器

能够用for循环的数据类型有以下几种: 一种是集合数据类型,如str,list,tuple,dict,set等;
一种是generator,包括生成器和带yieldgenerator function
这些直接可以用for循环的对象统称为可迭代的对象iterable,可以用isinstance()判断 一个对象是否是一个iterable对象:


>>> from collections import Iterable
>>> isinstance((),Iterable)
True
>>> isinstance([],Iterable)
True
>>> isinstance({},Iterable)
True
>>> isinstance('abc',Iterable)
True
>>> isinstance((x for x in range(10)),Iterable)
True
>>> isinstance(set,Iterable)
False
>>> isinstance(({},),Iterable)
True
>>> isinstance(100,Iterable)
False

迭代器(iterator)的实质是实现next()方法的对象,也就是说可以被next()函数调用并不断返回下一个值的对象,迭代其中重点关注两个方法:
__iter__()方法 :返回迭代器本身,python内建函数iter()本质上就是不断调用 该方法;
__next__()方法:当next()方法被调用的时候,迭代器会返回它的下一个值,如果next()方法被调用,但迭代器没有值可以返回,就会引发一个StopIteration异常,python 内建函数next()调用该方法。

>>> it = ([1,2,3,4,5])
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'list' object is not an iterator
>>> it = iter([1,2,3,4,5])
>>> next(it)
1
>>> it.__next__()
2
>>> next(it)
3
>>> next(it)
4
>>> next(it)
5
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

总结:
**1)**可迭代对象包含迭代器。
**2)**如果一个对象拥有__iter__()方法,其是可迭代对象;如果一个对象拥有next()方法,其是迭代器。
**3)**定义可迭代对象,必须实现__iter__()方法;定义迭代器,必须实现__iter__()next()方法。
4)list、dict、str虽然是Iterable(可迭代对象),却不是Iterator(迭代器),不过可以通过iter()函数获得一个Iterator对象,生成器都是Iterator对象。
**5)**凡是可作用于for循环的对象都是Iterable类型。
**6)**凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列。

  • 那么为什么list、dict、str等数据类型不是Iterator(迭代器)?

这是因为Python中的list、dict、str类型自身实现了一个__iter__()方法使其变成iterable
Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

生成器

生成器是一个特殊的迭代器,自动实现了迭代器协议(即__iter__()__next__()/next()方法),必须要再手动实现这两种方法。
创建生成器的方法有很多种:
1.只要把一个列表生成式的[]改成(),就创建了一个generator

>>> L = [2*x for x in range(10)]
>>> L
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
>>> g = (2*x for x in range(10))
>>> g
<generator object <genexpr> at 0x005AA670>

此时可以利用next()方法取出每一个值,不过一般都是用for循环来取出生成器的值。
2.生成是包含yield关键字的函数,看fib()函数:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'just do it'

要把fib()函数变成generator,只需要把print(b)改为yield
就可以了,如果一个函数中包含了yield关键字,那么这个函数就不再是一个普通函数,而是一个generator

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'just do it'

关键字yield是一个语法糖,内部实现支持了迭代器协议,同时yield内部是一个状态机,维护着挂起和继续的状态。

  • 生成器是怎么调用执行的呢?只需要了解下面几条规则即可:

1). 当生成器被调用的时候,函数体的代码不会被执行,而是会返回一个迭代器,其实,生成器函数返回生成器的迭代器。
“生成器的迭代器”这个术语通常被称作”生成器”。要注意的是生成器就是一类特殊的迭代器。作为一个迭代器,生成器必须要定义一些方法,其中一个就是next()。如同迭代器一样,我们可以使用next()函数来获取下一个值。需要明白的是,这一切都是在yield内部实现的。
2). 当next()方法第一次被调用的时候,生成器函数才开始执行,执行到yield语句处停止,next()方法的返回值就是yield语句处的参数(yielded value),当继续调用next()方法的时候,函数将接着上一次停止的yield语句处继续执行,并到下一个yield处停止;如果后面没有yield就抛出StopIteration异常。

3).每调用一次生成器的next()方法,就会执行生成器中的代码,直到遇到一个yield或者return语句。yield语句意味着应该生成一个值(在上面已经解释清楚)。return意味着生成器要停止执行,不再产生任何东西。

4). 生成器的编写方法和函数定义类似,只是在return的地方改为yield。生成器中可以有多个yield。当生成器遇到一个yield时,会暂停运行生成器,返回yield后面的值。当再次调用生成器的时候,会从刚才暂停的地方继续运行,直到下一个yield。生成器自身又构成一个循环器,每次循环使用一个yield返回的值,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值