larken

勤奋的人生才有价值

导航

生成器和迭代器

# yield 与 yield from
# 我们可以在函数中使用yield来产生值,表面上来看,yield有点像是return,不过函数并不会因为yield而结束,只是将流程控制权交给函数的调用者。
import sys
import random
def producer():
    while True:
        data=random.randint(0,9)
        print('生产了:',data)
        yield data  # 产生下一个值,流程回到调用者
def consumer():
    while True:
        data=yield  # 调用产生器send()方法时的产生值会成为yield 的运算结果
        print('消费了:',data)
def clerk(jobs,producer,consumer):
    print('执行{}次生产与消费'.format(jobs))
    p=producer()
    c=consumer()
    next(c) # 领消费者执行到yield处
    for i in range(jobs):
        data=next(p) # 获取生产者的生产值
        c.send(data)  # 将值传给消费者
clerk(int(sys.argv[1]),producer,consumer)
# yield可用来创建生成器。
def np_range(n):
    for i  in range(0-n,0):
        yield i
    for i in range(1,n+1):
        yield i
print(list(np_range(5)))
# 从Python3.4开始,新增了yield from语句,上面的程序片段可以直接改写为以下形式:
def np_range(n):
    yield from range(0-n,0)
    yield from range(1,n+1)
print(list(np_range(5)))

 

一、迭代器
可迭代对象(Iterable):从语法形式上讲,能调用__iter__方法的数据对象就是可迭代对象:
>>> [1,2,3].__iter__()
<listiterator object at 0x10221b150>
>>> {'name':'alvin'}.__iter__()
<dictionary-keyiterator object at 0x1022180a8>
>>> {7,8,9}.__iter__()
<setiterator object at 0x1021ff9b0>
迭代器对象的特性就是能够调用__next__方法依次计算出迭代器中的下一个值。
>>> s={1,2,3}
>>> i=s.__iter__()  # 返回可迭代对象s的迭代器对象i
>>> i.__next__()    # 从第一个元素开始,i通过__next__方法就可以得到可迭代对象s的下一个值。
1
>>> i.__next__()
2
>>> i.__next__()
3
>>> i.__next__()   #迭代结束,没有下一个值时调用__next__()抛出StopIteration的异常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
提示:可以用iter(s)取代s._iter__(),其实iter(s)本质就是在调用s.__iter__(),这与len(s)会调用s.__len__()是一个原理,同理,可以用next(i)取代i.__next__()。obj.__iter__()方法的调用后返回的就是一个迭代器对象
于是我们我们可以将之前基于索引的迭代换成迭代器的形式,如下:
#while的形式
l=['a','b','c','d','e']
i=iter(l)
while 1:
    try:
        print(next(i))
    except StopIteration:
        break
迭代器的优点:惰性计算,python中的Iterator对象只是在需要时才去不断调用next()来计算一个个值,需要时,临时计算出,之前是不存在的。就迭代器本身来说,同一时刻在内存中只有一个值,因而可以存放无限大的数据流
迭代器由于对内存存储的优化在py3中已经大量使用,我们可以通过isinstance()判断一个对象是否是Iterable和Iterator:
>>> from collections import Iterable,Iterator
>>> isinstance([1,2,3],Iterable)
True
>>> i=iter('abc')
>>> isinstance(i,Iterator)
True 
迭代器协议要求迭代对象具有__iter__()和__next__()两个方法,__iter__则是返回迭代器本身,目的是使for循环可以遍历迭代器对象

二生成器:简单说,生成器就是使用了yield关键字的函数:
>>> def countdown(n):
...      print('countdown start')
...      while n > 0:
...          yield n
...          n-=1
...      print('Done!')


>>> countdown
<function countdown at 0x102212f50>

>>> countdown(5)
<generator object countdown at 0x1021ff9b0>

>>> gen=countdown(3) # 验证对象是否为迭代器对象
>>> gen
<generator object countdown at 0x101be0a40>
    >>> from collections import Iterator
    >>> isinstance(gen,Iterator)
    True
>>> gen=countdown(3)
>>> gen.__next__()
countdown start
3
>>> gen.__next__()
2
>>> gen.__next__()
1
>>> gen.__next__()
Done!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> c=countdown(7)
>>> c
<generator object countdown at 0x10123f308>
>>> next(c)
countdown start
7
>>> next(c)
6
>>> c.close()
>>> next(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
yield的功能总结:
(1)封装iter和next方法
(2)执行函数时遇到yield返回其后的值,不同于return,yiled可以返回多次值
(3)挂起函数的状态,等待下一次调用next方法时找到对应的暂停位置继续执行。
创建一个生成器对象有两种方式,一是通过在函数中创建yield关键字来实现。另一种就是生成器表达式,这是一种类似于数据类型中学过的列表生成式的语法格式,只是将[]换成(),即:
(expression for item in iterable if condition)
不同于列表生成式最后返回一个列表结果,生成器表达式顾名思义会返回一个生成器对象,比如:

>>> [x*x for x in range(4)]  #
[0, 1, 4, 9]
>>> gen=(x*x for x in range(4))
>>> gen
<generator object <genexpr> at 0x101be0ba0>
当需要用到其中的值时,再通过调用next方法或者for循环将值一个个地计算出来:
>>> next(gen)
0
>>> next(gen)
1
>>> next(gen)
4
>>> next(gen)
9
>>> next(gen)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

 

posted on 2019-03-19 14:28  larken  阅读(211)  评论(0)    收藏  举报