迭代器(iterator)

重复做多次事情

  • 迭代器必须要有__next__方法(python2为next方法)
    【python3可以通过next函数访问__next__方法】
    该方法会返回迭代器的下一个值;若没有返回值或者没有值能返回了,会产生StopIteration异常
  • 实现迭代就调用对象的__iter__方法
  • 列表迭代器的缺点——
    必须一次性把所有值都计算出来(考虑range()和xrange())
    不适用于无穷元素的情况
  • 可迭代 和 迭代器
    实现了__iter__方法的对象是可迭代的
    实现了__next__方法的对象是迭代器
In [4]:
#裴波那契数列,每个数是前两个数的和
class Fibs():
    def __init__(self):
        self.a = 0
        self.b = 1
    def next(self):            #返回迭代器的下一个值
        self.a, self.b = self.b, self.a + self.b
        return self.a
    def __iter__(self):             #迭代,返回self
        return self
In [5]:
fibs = Fibs()
In [6]:
for f in fibs:
    if f > 1000:
        print f
        break
1597

In [7]:
#可以通过iter函数从可迭代的对象获取迭代器
it = iter([1,3,7])
In [8]:
it.next()
Out[8]:
1
In [9]:
it.next()
Out[9]:
3
In [10]:
it.next()
Out[10]:
7
In [11]:
it.next()         #已经没有值可以返回了
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-11-54f0920595b2> in <module>()
----> 1 it.next()

StopIteration: 
In [12]:
#从迭代器获得序列
In [16]:
class Fibs():
    def __init__(self):
        self.a = 0
        self.b = 1
    def next(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > 1000:                       #构造一个1000以内的裴波那契序列
            raise StopIteration             #序列元素必须是有穷的,引发异常StopIteration即可终止迭代
        else:
            return self.a
    def __iter__(self):
        return self
In [17]:
fib = Fibs()      #获取一个迭代器
In [18]:
list(fib)       #通过list函数显示地把迭代器构造为列表
Out[18]:
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]

生成器(简单生成器)

任何包含yield语句的函数都叫生成器
yield语句产生一个值,并且使函数暂停,等待下一次唤醒(再次调用)

创建生成器

In [24]:
nested = [[1,2], [3,4], [5]]
In [25]:
def flatten(nested):
    for sublist in nested:       #迭代列表中的列表
        for element in sublist:        #迭代最里层的元素
            yield element        #产生一个element值并且暂停,等到下一次唤醒会从原处继续执行
In [26]:
for num in flatten(nested):      #把生成器拿来迭代使用
    print num
1
2
3
4
5

In [27]:
#把生成器转换为列表 
list(flatten(nested))
Out[27]:
[1, 2, 3, 4, 5]

生成器推导式

列表推导式可以得到一个列表(一次性列出)
生成器推导式可以得到一个生成器(一个一个列出)
两者的关系类似与range()和xrange()的关系

In [28]:
g = ( (x, y, x * y) for x in range(1,9) for y in range(1, 9) )      #产生一个生成九九乘法表的生成器
In [29]:
g.next()       #和迭代器一样,有next方法
Out[29]:
(1, 1, 1)
In [30]:
g.next()
Out[30]:
(1, 2, 2)
In [31]:
#生成器推导式不一定要有独立的圆括号括起来,只要两边是圆括号,即使不是自己的,也能实现
sum(i ** 2 for i in range(10))      #这里两边的括号是属性求和函数sum的
                                                      #这里表示求10以内的平方和
Out[31]:
285

递归生成器

In [1]:
nested = [[1,2,3],4,[ [5,6],7 ],8]        #有任意层嵌套的列表
In [2]:
def flatten(nested):
    try:
        for sublist in nested:
            for element in flatten(sublist):     #嵌套
                yield element
    except TypeError:          #最里层的递归在执行第三行时,nested参数已经是元素,不可迭代
        yield nested
#字符串等对象(字符串、Unicode、UserString等)不适合迭代,此处无法处理字符串等对象
In [3]:
list( flatten(nested) )
Out[3]:
[1, 2, 3, 4, 5, 6, 7, 8]
In [4]:
#因为字符串等对象不适合迭代,所以我们应当加入一段检查语句
def flatten(nested):
    try:
        #检查是否为字符串等对象
        try:
            nested + ''        #尝试字符串连接
        except TypeError:
            pass
        else:
            raise TypeError            #如果成功连接了,说明nested是类似字符串的对象,引起TypeError,跳转到最后一行直接生成字符串
        
        for sublist in nested:
            for element in flatten(sublist):
                yield element
    except TypeError:
        yield nested
In [5]:
list( flatten(['aaaa', 'bbbb', [1,2,3], [[4,5],6],7]) )
Out[5]:
['aaaa', 'bbbb', 1, 2, 3, 4, 5, 6, 7]

通用生成器

生成器包括两个部分——生成器的函数 和 生成器的迭代器

In [6]:
def simple_generator():
    yield 1
In [7]:
simple_generator        #生成器的函数
Out[7]:
<function __main__.simple_generator>
In [8]:
simple_generator()           #生成器的迭代器
Out[8]:
<generator object simple_generator at 0x7fbaec227cd0>

生成器方法

  • send方法:与next方法类似,但它需要一个参数,它可以是任意对象,并且把这个对象作为参数传递给生成器并且返回结果
  • yield表达式:yield除了是语句还可以是表达式,而且通常这个表达式两边要用圆括号括起来
    如果生成器挂起期间调用了send方法,那么表达式取出的是send传递的对象
    如果生成器挂起期间没有调用send方法,那么表达式取出的是迭代器的下一个值(调用了next方法)
In [9]:
def repeater(value):
    while True:
        new = (yield value)    #yield表达式,两边最好有圆括号
        if new is not None: value = new            #如果new取到的不是空值,拿就把它赋给value方便下一次调用
In [10]:
r = repeater(42)
In [11]:
r.next()
Out[11]:
42
In [12]:
r.next()
Out[12]:
42
In [13]:
r.send('Hello world!')
Out[13]:
'Hello world!'
  • throw方法:使用异常类型调用,还有可选的值以及回溯对象,用于在生成器内的yield表达式中引发异常
  • close方法:无需参数,停止生成器

模拟生成器

旧版python并不能使用生成器,但我们可以模拟出大部分的生成器(只能有穷)

In [14]:
#改写flatten
def flatten(nested):
    result = []           #把这个列表作为迭代器
    try:
        try:
            nested + ''
        except TypeError:
            pass
        else:
            raise TypeError 
        
        for sublist in nested:
            for element in flatten(sublist):
                result.append(element)              #给result追加元素取代yield语句
    except TypeError:
        result.append(nested)               #取代yield语句
    return result          #把列表作为结果返回
In []: