返回顶部

[python]--迭代器,生成器补充

  在python中,list,string,dict都是可迭代对象,可以通过for语句遍历.

迭代器

  迭代器对象要求支持迭代器协议的对象,在python中,支持迭代器协议就算实现对象的__iter__()和next()方法.其中__iter__()方法返回迭代器对象本身;

  next()方法返回容器的下一个元素,在结尾时引发StopIteration异常

  __iter__()和next()方法

  这两个方法是迭代器最基本的方法,一个用来获得迭代器对象,一个用来获取容器中的下一个元素.

  对于可迭代对象,可以使用内建函数iter()来获取它的迭代器对象:

  

  iter()方法可以获得list的迭代器对象,然后就可以使用__next__()方法来访问list中的元素了.当容器中没有可访问的元素后,__next__()方法就会抛出一个

  StopIteration异常终止迭代器.

  不使用iter()方法的话,可以使用next()方法对可迭代对象,来获取下一个元素

  自定义迭代器

    了解了迭代器协议之后,就可以自定义迭代器了.

    下面例子中实现了一个MyRange的类型,这个类型中实现了__iter__()方法,通过这个方法返回对象本身作为迭代器对象;同时,实现了next()方法用来获取容器中的下一个元素,当没有可访问元素后,就抛出StopIteration异常.

    

class MyRange(object):
    def __init__(self, n):
        self.idx = 0
        self.n = n
        
    def __iter__(self):
        return self
        
    def next(self):
        if self.idx < self.n:
            val = self.idx
            self.idx += 1
            return val
        else:
            raise StopIteration()

 

  这个自定义类型跟内建函数xrange很类似,看一下运行结果:

    

  生成器

  在python中,使用生成器可以很方便的支持迭代器协议.生成器通过生成器函数产生,生成器函数可以通过常规的def语句来定义,但是不用return返回,而是用yield一次返回一个结果,在每个结果之间挂起和继续它们的状态,来自动实现迭代协议.

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

  下面看看生成器的使用:

  

  在这个例子中,定义了一个生成器函数,函数返回一个生成器对象,然后就可以通过for语句进行迭代访问.

  其实,生成器函数返回生成器的迭代器,"生成器的迭代器"这个术语通常被称作"生成器".要注意的是生成器就算一类特殊的迭代器.作为一个迭代器,生成器必须要定义一些方法,其中一个就是next().如同迭代器一样,我们可以使用next()函数获取下一个值.

  生成器执行流程

  • 当调用生成器函数的时候,函数只是返回了一个生成器对象,并没有执行.
  • 当next()方法第一次被调用的时候,生成器函数才开始执行,执行到yield语句处停止
    •   next()方法的返回值就是yield语句处的参数(yielded value)    
  • 当继续调用next()方法的时候,函数将接着上一次停止的yield语句处继续执行,并到下一个yield处停止;如果后面没有yield就抛出StopIteration异常

生成器表达式

  在开始介绍生成器表达式之前,先看看我们比较熟悉的列表解析(List comprehensions),列表解析一般都是下面的形式.

  [expr for iter_var in iterable if cond_expr]

  迭代iterable里所有内容,每一次迭代后,把iterable里满足cond_expr条件的内容放到iter_var中,再在表达式expr中应该iter_var的内容,最后用表达式的计算值生存一个列表.

  例如,生成一个list来保存50以内的所有奇数:

  [i for i in range(50) if i%2]

  生成器表达式是在python2.4中引入的,当序列过长,而每次只需要获取一个元素时,应当考虑使用生成器表达式而不是列表解析.生成器表达式的语法和列表解析一样,只不过生成器表达式是被()括起来的,而不是[],如下:

  (expr for iter_var in iterable if cond_expr)

  生成器表达式并不是创建一个列表,而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目"产生"(yield)出来,生成器表达式使用惰性计算,只有在检索时才被赋值,所以在列表比较长的情况下使用内存上更有效

  生成器表达式产生的生成器,自身是一个可迭代对象,同时也是迭代器本身

  递归生成器

    生成器可以向函数一样进行递归使用,下面看一个简单的例子,对一个序列进行全排列:

  

def permutations(li):
    if len(li) == 0:
        yield li
    else:
        for i in range(len(li)):
            li[0], li[i] = li[i], li[0]
            for item in permutations(li[1:]):
                yield [li[0]] + item
    
for item in permutations(range(3)):
    print item

 

  生成器的send()和close()方法

    生成器中还有两个很重要的方法:send()和close()

    send(value):

      从前面了解到,next()方法可以恢复生成器状态并继续执行,其实send()是除next()外另一个恢复生成器的方法.

      python2.5中,yield语句变成了yield表达式,也就是说yield可以有一个值,而这个值就是send()方法的参数,所以send(None)和next()是等效的.同样,next()和send()的返回值都是yield语句处的参数

      关于send():调用send传入非None值前,生成器必须处于挂起状态,否则将抛出异常

    close():

      这个方法用来关闭生成器,对关闭的生成器后再调用next或send将抛出异常

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

posted @ 2017-03-29 22:51  Will_D_Zhang  阅读(258)  评论(0编辑  收藏  举报