首页 > 学院 > 开发设计 > 正文

迭代器与生成器

2019-11-08 02:26:05
字体:
来源:转载
供稿:网友

一、迭代器

数学中的迭代可以指函数迭代的过程,即反复地运用同一函数计算,前一次迭代得到的结果被用于作为下一次迭代的输入。 即使是看上去很简单的函数,在经过迭代之后也可能产生复杂的行为,衍生出具有难度的问题。——引自维基百科

0. 迭代器协议

  __iter__()next()这两个方法一起构成了迭代器协议。即满足了这个两个方法才能称之为迭代器。

1. 可迭代的对象

  在Python中一样也有迭代的概念。比如循环打印出列表中的元素,这就是一个迭代的过程。在Python中,不仅可以对列表、元组、字典等进行迭代,还可以对其他对象迭代,前提是这个对象实现了 __iter__方法,这个对象也称为可迭代的对象。

2. 迭代器

  具有next方法的的可迭代对象iter方法会返回一个迭代器(__iter__返回的是迭代器本身)。在调用next方法时,迭代器会返回它的下一个值,如果next方法调用后,但是又没有值返回,就会引发StopIteration异常。

3. 示例1

>>> lists = [i for i in range(10)]# 可迭代的对象还有__iter__方法>>> dir(lists)['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__',........]# 获得迭代器>>> iters = lists.__iter__()>>> for i in range(len(lists)):... PRint iters.next(), ... 0 1 2 3 4 5 6 7 8 9

如果继续调用,就会引发StopIteration异常

>>> iters.next()Traceback (most recent call last): File "<stdin>", line 1, in <module>StopIteration

内建函数iter()可以从可迭代的对象中获得迭代器;list()含糊可以将迭代器显示的以列表的形式打印

>>> test = [i for i in range(5)]>>> iters = iter(test)>>> list(iters)[0, 1, 2, 3, 4]

4. 示例2—自定义迭代器

#!/usr/bin/pythonclass Fibs: value = 0 def __init__(self,ranges): self.ranges = ranges def next(self): self.value += 1 if self.value == self.ranges: raise StopIteration return self.value def __iter__(self): return selfIn [7]: from iters import FibsIn [8]: fib = Fibs(10)In [9]: list(fib)Out[9]: [1, 2, 3, 4, 5, 6, 7, 8, 9]

接下来验证是否为迭代器

In [2]: from iters import FibsIn [3]: from collections import Iterator In [4]: from collections import Iterable # 是迭代器对象In [5]: isinstance(iter(Fibs(10)), Iterator)Out[5]: True#可迭代的对象In [6]: isinstance(iter(Fibs(10)), Iterable)Out[6]: True

注释掉next()方法

#!/usr/bin/pythonclass Fibs: value = 0 def __init__(self,ranges): self.ranges = ranges #def next(self): #self.value += 1 #if self.value == self.ranges: raise StopIteration #return self.value def __iter__(self): return self

此时结果如下,没有next()方法就不再是iterator了:

In [3]: from iters import Fibs#是可迭代的对象In [4]: isinstance(iter(Fibs(10)), Iterable)Out[4]: TrueIn [5]: from collections import Iterator#不是迭代器对象In [6]: isinstance(iter(Fibs(10)), Iterator)Out[6]: False

二、生成器

A function which returns an iterator. It looks like a normal function except that it contains yield statements for producing a series a values usable in a for-loop or that can be retrieved one at a time with the next() function. Each yield temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator resumes, it picks-up where it left-off (in contrast to functions which start fresh on every invocation).

生成器是一个函数(任何包含yield语句的函数就是生成器)这个函数的返回值是迭代器yield语句功能是为了在for循环中产生一系列的值;或者是使用next()一次检索一个值每次产生一个值,函数就会被冻结:即函数停在那点等待被重新唤醒。函数被重新唤醒后就从停止的那点开始执行。

定义生成器的两种方式: - yield声明 - 生成器表达式

1. yield语句

In [57]: def sums(ranges): ....: test = 0 ....: for i in range(ranges): ....: test += i ....: yield test ....: # next()方法一次检索一个值In [58]: sums(10).next()Out[58]: 0In [59]: sums(10).next()Out[59]: 0#for循环产生一系列的值In [60]: for i in sums(10): ....: print i, ....: 0 1 3 6 10 15 21 28 36 45In [61]: sums(10)Out[61]: <generator object sums at 0x20ca190>

2. 生成器表达式

生成器表达式返回的是生成器;生成器定义的方法类似于列表推导式,但是生成器用的是圆括号;list()方法可以把生成器以列表的形式显示出来;

In [53]: g = (i for i in range(10))In [54]: gOut[54]: <generator object <genexpr> at 0x20c5eb0>In [55]: list(g)Out[55]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

生成器表达式也可以在当前的圆括号内直接使用。比如在函数调用中就不需要增加另外一个圆括号了。 如果需要生成大量的的值再迭代计算,最好不要使用列表推导式,而是使用生成器推导式直接迭代。因为列表推导式会实例化一个列表,从而丧失迭代的优势。

In [50]: sum(i for i in range(10)) Out[50]: 45

3. 生成器表达式和列表推导式的性能对比

In [41]: time sum([i for i in range(10000000)])CPU times: user 0.49 s, sys: 1.79 s, total: 2.28 sWall time: 2.28 sOut[41]: 49999995000000In [42]: time sum(i for i in range(10000000)) CPU times: user 0.65 s, sys: 0.00 s, total: 0.65 sWall time: 0.64 sOut[42]: 49999995000000

通过这个例子可以看出,在大量数据迭代的情况下,生成器表达式比列表推导式的性能好。因为列表推导式实实在在的生成了一个列表,在这里个例子中,先生成列表再求和,已经是2层for循环了,自然就要慢一点了。

通过这个例子顺便再对比一下xrange()和range()的性能吧

In [62]: time sum(i for i in xrange(10000000)) CPU times: user 0.50 s, sys: 0.00 s, total: 0.50 sWall time: 0.50 sOut[62]: 49999995000000In [63]: time sum(i for i in range(10000000)) CPU times: user 0.59 s, sys: 0.60 s, total: 1.19 sWall time: 1.19 sOut[63]: 49999995000000
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表