构建一个基本的Python迭代器

如何在python中创建迭代函数(或迭代器对象)?


python中的迭代器对象符合迭代器协议,这意味着它们提供了两种方法: __iter__()next()__iter__返回迭代器对象,并在循环开始时隐式调用。 next()方法返回下一个值,并在每个循环增量中隐式调用。 next()会在没有更多值返回时引发StopIteration异常,这是通过循环结构隐式捕获以停止迭代。

以下是一个简单的计数器示例:

class Counter:
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        return self

    def next(self): # Python 3: def __next__(self)
        if self.current > self.high:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1


for c in Counter(3, 8):
    print c

这将打印:

3
4
5
6
7
8

如前面的回答所述,使用生成器可以更轻松地进行编写:

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

for c in counter(3, 8):
    print c

打印的输出将是相同的。 在引擎盖下,生成器对象支持迭代器协议,并执行与类Counter类似的操作。

David Mertz的文章“迭代器和简单生成器”是非常好的介绍。


有四种方法来构建迭代函数:

  • 创建一个生成器(使用yield关键字)
  • 使用生成器表达式(genexp)
  • 创建一个迭代器(定义__iter____next__ (或Python 2.x中的next ))
  • 创建一个Python可以自己迭代的函数(定义__getitem__
  • 例子:

    # generator
    def uc_gen(text):
        for char in text:
            yield char.upper()
    
    # generator expression
    def uc_genexp(text):
        return (char.upper() for char in text)
    
    # iterator protocol
    class uc_iter():
        def __init__(self, text):
            self.text = text
            self.index = 0
        def __iter__(self):
            return self
        def __next__(self):
            try:
                result = self.text[self.index].upper()
            except IndexError:
                raise StopIteration
            self.index += 1
            return result
    
    # getitem method
    class uc_getitem():
        def __init__(self, text):
            self.text = text
        def __getitem__(self, index):
            result = self.text[index].upper()
            return result
    

    要查看所有四种实际操作方法:

    for iterator in uc_gen, uc_genexp, uc_iter, uc_getitem:
        for ch in iterator('abcde'):
            print ch,
        print
    

    其结果是:

    A B C D E
    A B C D E
    A B C D E
    A B C D E
    

    注意

    两种生成器类型( uc_genuc_genexp )不能reversed() ; 普通迭代器( uc_iter )将需要__reversed__魔法方法(它必须返回一个新的迭代器向后); 并且getitem可迭代( uc_getitem )必须具有__len__魔法方法:

        # for uc_iter
        def __reversed__(self):
            return reversed(self.text)
    
        # for uc_getitem
        def __len__(self)
            return len(self.text)
    

    要回答Panic上校关于无限懒惰评估迭代器的第二个问题,下面是使用上述四种方法中的每一种的例子:

    # generator
    def even_gen():
        result = 0
        while True:
            yield result
            result += 2
    
    
    # generator expression
    def even_genexp():
        return (num for num in even_gen())  # or even_iter or even_getitem
                                            # not much value under these circumstances
    
    # iterator protocol
    class even_iter():
        def __init__(self):
            self.value = 0
        def __iter__(self):
            return self
        def __next__(self):
            next_value = self.value
            self.value += 2
            return next_value
    
    # getitem method
    class even_getitem():
        def __getitem__(self, index):
            return index * 2
    
    import random
    for iterator in even_gen, even_genexp, even_iter, even_getitem:
        limit = random.randint(15, 30)
        count = 0
        for even in iterator():
            print even,
            count += 1
            if count >= limit:
                break
        print
    

    其结果(至少对我的样本运行):

    0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54
    0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
    0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
    0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32
    

    首先,itertools模块对于迭代器会很有用的各种情况都非常有用,但是您需要在python中创建一个迭代器:

    产量

    这不是很酷吗? 产量可以用来取代函数中的正常返回 。 它返回的对象是一样的,但不是销毁状态并退出,而是为了当你想执行下一次迭代时保存状态。 下面是一个直接从itertools函数列表中拉出来的例子:

     def count(n=0):
         while True:
             yield n
             n += 1
    

    正如函数描述中所述(它是itertools模块的count()函数...),它会生成一个迭代器,它返回从n开始的连续整数。

    发生器表达式是一个完整的蠕虫(真棒蠕虫!)。 它们可以用来代替列表理解来节省内存(列表解析在内存中创建一个列表,如果没有分配给一个变量,它将在使用后被销毁),但是生成器表达式可以创建一个生成器对象...这是一种奇特的方式说迭​​代器)。 这是一个生成器表达式定义的例子:

    gen = (n for n in xrange(0,11))
    

    这与我们上面的迭代器定义非常相似,除了整个范围预定在0和10之间。

    我刚刚发现xrange() (惊讶我以前没见过......)并将其添加到上面的示例中。 xrange()range()的可迭代版本,其优点是不会预建列表。 如果你有一个巨大的数据集来迭代,并且只有很多内存来完成它,那将是非常有用的。

    链接地址: http://www.djcxy.com/p/5139.html

    上一篇: Build a Basic Python Iterator

    下一篇: How to import an SQL file using the command line in MySQL?