生成器、迭代器

1、列表生成式
a = [i**2 for i in range(10)]
a为 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

2、生成器
定义:一边循环一边计算后面元素的机制,称为生成器(generator)。
生成器创建方法
  1、g = (i**2 for i in range(10))
打印generation元素,需要用到next()方法
next(g) 一次只能打印一个,要想再次打印就在next(g)
当next(g)打印完最后一个元素后,在使用next(g),解释器则会报错。
可以用for循环进行遍历,将所有元素都打印出来,也可以打印出自己需要的元素
for i in g:
    if i > 10
        print(i)

  2、用函数来创建生成器,使用yield
  以斐波那契数列举例 0,1,1,2,3,5,8,13,21,34,55...
  当不使用生成器的:

def fibo(n):
    before,now = 0, 1
    sum = 0
    count = 0
    while count < n:
        sum = before + now
        before = now
        now = sum
        count += 1
        print(before)

   如果使用生成器的话,只需要将print(before)变成yield before就可以了

def fibo(n):
    before,now = 0, 1
    sum = 0
    count = 0
    while count < n:
        sum = before + now
        before = now
        now = sum
        count += 1
        yield before
g_fibo = fibo(8)

调用生成元素可以用g_fibo.__next__()
当使用一次g_fibo.__next__(),函数运行就会暂停在yield位置,你就可以做其他的事情,当再次遇到g_fibo.__next__(),函数就会又开始执行,函数不断的暂停,开始。

用生成器模拟并发编程

import time
def consumer(name):
    print("{0}准备开始吃东西".format(name))
    while True:
        meat = yield  # 将外面传过来的值赋值给meat
        print("肉{0}来了,肉被{1}吃了".format(meat, name))
c1 = consumer("xu1")
c2 = consumer("xu2")
c1.__next__()
c2.__next__()
print("--------------大帅哥要开始做肉啦----------")
for i in range(10):
    time.sleep(1)
    print("做了2盘肉")
    c1.send(i) # 将 i 传到yield处
    c2.send(i)

调用send(x)给生成器传值时,必须确保生成器已经执行过一次next()调用, 这样会让程序走到yield位置等待外部第2次调用。

3、迭代器


我们已经知道,可以直接作用于for循环的数据类型有以下几种:

    一类是集合数据类型,如list、tuple、dict、set、str等;

    一类是generator,包括生成器和带yield的generator function。

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable,可迭代的意思就是可遍历、可循环。

可以使用isinstance()判断一个对象是否是Iterable对象:

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

而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

*可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。

可以使用isinstance()判断一个对象是否是Iterator对象:

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

生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。

把list、dict、str等Iterable变成Iterator可以使用iter()函数:

>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), 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对象。


--------借鉴于old_boy(老男孩教育)-------------------

posted @ 2019-09-24 19:22  虚xu  阅读(158)  评论(0编辑  收藏  举报