构build一个基本的Python迭代器

如何在Python中创build一个迭代函数(或迭代器对象)?

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

这是一个简单的计数器例子:

 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的文章“ 迭代器和简单生成器 ”是一个很好的介绍。

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

  • 创build一个生成器(使用yield关键字 )
  • 使用生成器expression式( genexp )
  • 创build一个迭代器(定义__iter____next__ (或Python 2.x中的next ))
  • 创build一个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 

其结果是:

 ABCDE ABCDE ABCDE ABCDE 

注意

两个生成器types( uc_genuc_genexp )不能reversed() ; 普通迭代器( uc_iter )将需要__reversed__魔术方法(它必须返回一个新的迭代器向后)。 而getitem iteratable( 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 

其结果(至less对我的样品运行):

 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中创build一个迭代器:

产量

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

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

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

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

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

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

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

我看到你们中的一些人在__iter__ return self 。 我只是想指出, __iter__本身可以是一个生成器(从而消除__next__和提高StopIterationexception的需要)

 class range: def __init__(self,a,b): self.a = a self.b = b def __iter__(self): i = self.a while i < self.b: yield i i+=1 

当然这里也可以直接生成一个生成器,但是对于更复杂的类,它可能是有用的。

这个问题是关于可迭代的对象,而不是迭代器。 在Python中,序列也是可迭代的,所以制作一个可迭代的类的一个方法就是使它的行为像一个序列,即赋予它__getitem____len__方法。 我已经在Python 2和3上testing了这个。

 class CustomRange: def __init__(self, low, high): self.low = low self.high = high def __getitem__(self, item): if item >= len(self): raise IndexError("CustomRange index out of range") return self.low + item def __len__(self): return self.high - self.low cr = CustomRange(0, 10) for i in cr: print(i) 

这是一个没有yield的迭代函数。 它使用了iter函数和一个闭包,它将它的状态保存在python 2的封闭范围内的一个可变( list )中。

 def count(low, high): counter = [0] def tmp(): val = low + counter[0] if val < high: counter[0] += 1 return val return None return iter(tmp, None) 

对于Python 3,封闭状态在封闭范围内保持不变, nonlocal局部范围在本地范围内用于更新状态variables。

 def count(low, high): counter = 0 def tmp(): nonlocal counter val = low + counter if val < high: counter += 1 return val return None return iter(tmp, None) 

testing;

 for i in count(1,10): print(i) 1 2 3 4 5 6 7 8 9