手记

Python 生成器和迭代器

生成器和迭代器


生成器


在上文中简单介绍了生成器的作用。在列表推导创建列表时,受到内存的限制,列表容量同样会受到限制,而且会极大的消耗空间。这时,一边循环一边计算这种机制,也就是生成器也就出现了。

创建生成器

创建生成器(generator),有很多方法。其中一种,使用生成器表达式,即是将列表推导中的方括号 [] 改成圆括号 ()

如下示例:

>>> L = [x * x for x in range(10)] 
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10)) 
>>> g
<generator object <genexpr> at 0x000001FBFD43A048>
>>> list(g)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

由于生成器返回的结果是惰性序列,并不会直接返回想要结果,可以手动遍历,一个个进行打印:

>>> g = (x * x for x in range(10))
>>> next(g) 
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

调用 next() 可以返回 g 的下一个元素值,当没有更多元素的时候,会抛出 StopIteration 的错误。若只是遍历获得结果,可以使用 for 循环,因为 generator 是可迭代对象:

>>> g = (x * x for x in range(10))
>>> for i in g: 
...     print(i) 
... 
0
1
4
9
16
25
36
49
64
81

使用 for 循环遍历的时,就不需要担心 StopIteration 异常。

还有另外一种方法可以创建生成器(generator),在 Python 中,使用 yield 的函数被称为生成器(generator)函数。使用 yield 实现斐波那契数列,如下示例:

def fib(n):
	ct, a, b = 0, 0, 1
	while ct < n:
		yield b
		a, b = b, a + b
		ct += 1
	return "done"

生成器和普通函数执行过程有所不同。函数是顺序执行,遇到 return 会返回结果。而生成器遇到 next() 会执行,遇到 yield 返回,再次执行会直接在上次返回的 yield 继续执行。如下示例:

>>> def test():
...     print("value 1")
...     yield(1)
...     print("value 2")
...     yield(2)
...     print("value 3")
...     yield(3)
... 
>>> t = test()
>>> next(t)
value 1
1
>>> next(t)
value 2
2
>>> next(t)
value 3
3
>>> next(t)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

上述代码中,调用 next() 后,打印 value 1,遇到 yield 输出 1,停止执行,再次执行 next() 时,在上次遇到 yield 语句停止的后面继续执行,输出 value 22,直到没有元素,抛出异常。这就是生成器函数的执行过程。

迭代器


开始迭代器的内容前,先延伸介绍一个可迭代对象的概念。可以直接作用于 for 循环的对象称为可迭代对象:Iterable 。可以用 isinstance() 进行判断:

>>> from collections import Iterable
>>> isinstance('abc', Iterable) 
True
>>> isinstance((x for x in range(10)), Iterable) 
True
>>> isinstance(100, Iterable)
False

可以被 next() 函数调用并不断返回下一个值得对象称为迭代器:Iterator。同样可以用 isinstance() 判断对象是否是 Iterator 对象:

>>> from collections import Iterator 
>>> isinstance('abc', Iterator)
False
>>> isinstance((x for x in range(10)), Iterator) 
True

可以看出,生成器也是 Iterator 对象。但 str 虽然是 Iterable,却不是 Iterator,同样的还有列表 list,字典 dict

iter() 函数能够将 Iterable 变成 Iterator,例如:

>>> isinstance(iter('abc'), Iterator) 
True
>>> isinstance(iter([]), Iterator)    
True

创建一个迭代器

创建迭代器需要实现两个方法:__iter__()__next__()

__iter__() 方法返回一个特殊的迭代器对象,这个对象能够实现 __next__() 方法通过 StopIteration 异常标识迭代的完成。

__next__() 方法会返回下一个迭代器对象。

实现逐步加 1 的类,示例如下:

>>> class NumberIncrease(): 
...     def __iter__(self): 
...         self.x = 1      
...         return self     
...     def __next__(self): 
...         i = self.x      
...         self.x += 1
...         return i
... 
>>> num_increase = NumberIncrease()
>>> num_iter = iter(num_increase)
>>> next(num_iter)
1
>>> next(num_iter)
2
>>> next(num_iter)
3
>>> next(num_iter)
4
>>> next(num_iter)
5

以上就是本篇的主要内容


1人推荐
随时随地看视频
慕课网APP