python 迭代器和生成器

1.迭代器

在python中,有很多数据类型是属于可迭代对象。比如,元祖,列表,字典,字符串,文件等类型。
python标准的迭代器对象实现需要支持两个方法来实现迭代协议:

__iter__ returns the iterator object itself. This is used in for and in statements.
__next__ method returns the next value from the iterator.
 If there is no more items to return then it should raise StopIteration exception.

内建函数iter()接收一个可迭代对象,返回一个迭代器。迭代器以类的方式实现,比如下面是内建的函数xrange:

class yrange:
    def __init__(self, n):
        self.i = 0
        self.n = n

    def __iter__(self):
        return self

    def next(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()

__iter__方法是实现一个可迭代对象的,在后台,iter函数就是调用给定对象上的_iter__函数。__iter__的返回值是一个迭代器,它要有一个next方法,并在没有下一个元素的时候报StopIteration错误。注意:一个迭代器对象只能使用一次,这就意味着,当一个迭代器对象在调用next方法时,raise StopIteration的错误后,再调用next方法,还是会报同样的错误。

class zrange:
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        return zrange_iter(self.n)

class zrange_iter:
    def __init__(self, n):
        self.i = 0
        self.n = n

    def __iter__(self):
        # Iterators are iterables too.
        # Adding this functions to make them so.
        return self

    def next(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()

注意: 迭代器和可迭代对象有什么区别和联系呢?

  • 可迭代的对象有个 iter 方法,每次都实例化一个新的迭代器;而迭代器要实现 next 方法,返回单个元素,此外还要实现 iter 方法,返回迭代器本身。
    因此,迭代器是可迭代的,但是,可迭代对象不是迭代器。
  • 可迭代对象一定不能是自身的迭代器,也就是说,可迭代对象必须实现__iter__方法,但是不能实现__next__方法。另一方面,迭代器应该一直可以迭代,迭代器的__iter__方法应该返回自身。

2.生成器

只要在函数的定义体中有yield关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象,也就是说,生成器函数时生成器工厂。

def yrange(n):
    i = 0
    while i < n:
        yield i
        i += 1

每次yield语句执行,都会产生一个新的值.所以,一个生成器也是一个迭代器,不用担心迭代协议。
当一个生成器函数被调用时,它返回一个生成器对象,当next方法被第一次调用时,函数开始执行直到yield语句,yield的值会在next方法调用时返回。

>>> def foo():
...     print "begin"
...     for i in range(3):
...         print "before yield", i
...         yield i
...         print "after yield", i
...     print "end"
...
>>> f = foo()
>>> f.next()
begin
before yield 0
0
>>> f.next()
after yield 0
before yield 1
1
>>> f.next()
after yield 1
before yield 2
2
>>> f.next()
after yield 2
end
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

例子:

def integers():
    """Infinite sequence of integers."""
    i = 1
    while True:
        yield i
        i = i + 1

def squares():
    for i in integers():
        yield i * i

def take(n, seq):
    """Returns first n values from the given sequence."""
    seq = iter(seq)
    result = []
    try:
        for i in range(n):
            result.append(seq.next())
    except StopIteration:
        pass
    return result

print take(5, squares()) # prints [1, 4, 9, 16, 25]

类形式的生成器(该类型的生成器是可重用的):

>>> class Counter(object):
...     def __init__(self, low, high):
...         self.low = low
...         self.high = high
...     def __iter__(self):
...          counter = self.low
...          while self.high >= counter:
...              yield counter
...              counter += 1
...
>>> gobj = Counter(5, 10)
>>> for num in gobj:
...     print(num, end=' ')
...
5 6 7 8 9 10
>>> for num in gobj:
...     print(num, end=' ')
...
5 6 7 8 9 10

但是,像下面这样的例子,一个生成器对象是不能重复使用的。

def counter_generator(low, high):
    while low <= high:
       yield low
       low += 1

if __name__ == '__main__':

    cg = counter_generator(5, 10)

    for i in cg:
        print i

    for i in cg:
        print i
# 第一个for循环可以打印出结果,但是第二个不能
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。