码迷,mamicode.com
首页 > 编程语言 > 详细

Python生成器(generator)和迭代器(Iterator)

时间:2019-03-21 21:47:16      阅读:180      评论:0      收藏:0      [点我收藏+]

标签:mos   生成式   无法   就是   包子   import   生成   内存限制   pytho   

列表生成式

a = [i+1 for i in range(10)]
print(a)

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

这就是列表生成式

生成器(generator)

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。

如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的    list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(generator)。

# 列表生成式:
L = [x * x for x in range(10)]
print(L)
# 生成器:
g = (x * x for x in range(10))
print(g)

输出结果:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<generator object <genexpr> at 0x00000267824C0258>

list可以直接打印出结果,但是generator只能通过next()获得下一个返回值

g = (x * x for x in range(4))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))

输出结果:

0
1
4
9

Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
StopIteration

generator保存的是算法,每次调用next(g)就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

生成器示例:

def fib(max_n):                            # 斐波那契数列生成器
    n, a, b = 0, 0, 1
    while n < max_n:
        yield b                            # 函数中出现yield则函数变成生成器
        a, b = b, a+b 
        n = n+1
    return "done"

f = fib(6)
print(f.__next__())                        # 生成器生成一个数(f.__next__()=next(f))
print("______")                            # 生成器无法返回,之后生成的数据不包含以上的数

while True:
    try:
        x = next(f)                        # 循环生成数列
        print("f:", x)                     # 输出计算结果
    except StopIteration as e:             # 当循环次数大于max_n时,运行以下
        print("Generator return value:", e.value)
        break

输出结果:

1
______
f: 1
f: 2
f: 3
f: 5
f: 8

当函数中出现 ‘yield’ 时这个函数就成了一个 generator 的函数

generator在执行的时候遇到 yield 时会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

 

通过yield实现在单线程的情况下实现并发运算的效果:

import time


def consumer(name):
    print("%s开始吃包子了" % name)    
    while True:
        produce = yield                                # 函数在此暂停,等待唤醒
        print("%s吃了%i个包子" % (name, produce+1))      # 唤醒后执行


def producer(name):
    c = consumer("A")
    c2 = consumer("B")
    c.__next__()    
    c2.__next__()
    print("%s准备开始生产" % name)
    for i in range(3):
        time.sleep(1)
        print("已经做了%i个包子" % (i+1))
        c.send(i)                                       # 将i发送给produce,并唤醒函数
        c2.send(i)

producer("C")

输出结果:

A开始吃包子了
B开始吃包子了
C准备开始生产
已经做了1个包子
A吃了1个包子
B吃了1个包子
已经做了2个包子
A吃了2个包子
B吃了2个包子
已经做了3个包子
A吃了3个包子
B吃了3个包子

在 producer 函数中 c 和 c2 轮流调用 consumer 函数

send() 和 next() 一样可以唤醒生成器,而且还能给 yield 传值

 

迭代器

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

  • 一类是集合数据类型,如list、tuple、dict、set、str等;
  • 一类是generator,包括生成器和带 yield 的 generator function。

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。

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

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

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

 

生成器都是Iterator对象,但list、dict、str 虽然是 Iterable ,却不是Iterator。
把list、dict、str 等 Iterable 变成Iterator可以使用 iter() 函数.


 

 

 

Python生成器(generator)和迭代器(Iterator)

标签:mos   生成式   无法   就是   包子   import   生成   内存限制   pytho   

原文地址:https://www.cnblogs.com/dbf-/p/10574741.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!