别摸我
别摸我
文章目录
  1. Python中的generator、Iterator和Iterable
    1. 可迭代对象(Iterable)
    2. 生成器(generator)
    3. 生成器表达式(generator expression)
      1. generator function
    4. 迭代器(Iterator)

Python中的generator、Iterator和Iterable

Python中的generator、Iterator和Iterable

可迭代对象(Iterable)

在Python中,迭代是通过 for ... in 来完成的。可以直接用于 for 循环的数据类型有:

  • 集合数据类型:listtupledictsetstr
  • generator,包括生成器和带 yield 的generator function.

这些可以直接用于 for 循环的对象统称为可迭代对象:Iterable

可以使用isintance()判断一个对象是否是 Iterable 对象:

1
2
3
4
5
6
7
8
9
10
11
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('fuck', Iterable)
True
>>> isinstance((x for x in range(16) if x%2 == 0)), Iterable)
True
>>> isinstance(123, Iterable)
False

生成器(generator)

生成器表达式(generator expression)

列表解析可以很方便创建一个列表,但是,当创建一个很大的列表而且我们仅仅需要用到前几个元素的时候,不仅会占用很大的内存,而且后面不需要用到的元素占用的空间也都浪费了。

为此,Python中引入了一种机制;叫作生成器:generator

1
2
3
4
5
6
>>> l = [x * x for x in range(4)]
>>> l
[0, 1, 4, 9]
>>> g = (x * x for x in range(4))
>>> g
<generator object <genexpr> at 0x7f7dd1a53ba0>

我们把列表解析的[]改成(),就创建了一个 generator。这里,l是一个 list,而 g 是一个generator

我们可以直接获取list的每一个元素,但要获取generator的每一个元素,要通过 next() 函数:

1
2
3
4
5
6
7
8
9
10
11
12
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

每次调用 next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration 的错误。

当然,实际中我们不可能通过 next() 来一个一个获取里面的元素,而是通过 for 循环,因为generator 也是一个 可迭代对象:

1
2
3
4
5
6
7
8
>>> g = (x * x for x in range(4))
>>> for n in g:
... print(n)
...
0
1
4
9

所以,当我们创建一个generator后,基本永远不会用 next(),而是通过 for 循环来迭代它,并且不用关心 StopIteration 错误。

generator function

来看一个生成斐波那契数列的generator function

1
2
3
4
5
6
7
def fib(num):
n, a, b = 0, 0, 1
while n < num:
yield b
a, b = b, a + b
n = n + 1
return 'done'

这里的函数包含了一个 yield 关键字,那么这个函数就不是普通函数而是一个generator:

1
2
3
>>> f = fib(4)
>>> f
<generator object fib at 0x7f7dd04e9570>

使用 for 循环来迭代:

1
2
3
4
5
6
7
8
>>> for n in fib(5):
... print(n)
...
1
1
2
3
5

但是用 for 循环调用generator时,发现拿不到generator的 return 语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在 StopIterationvalue 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> g = fib(5)
>>> while True:
... try:
... x = next(g)
... print('g:', x)
... except StopIteration as e:
... print('Generator return value:', e.value)
... break
...
g: 1
g: 1
g: 2
g: 3
g: 5
Generator return value: done

迭代器(Iterator)

可以被 next() 函数调用并不断返回下一个值的对象称为迭代器:Iterator

可以使用isinstance()判断一个对象是否是 Iterator 对象:

1
2
3
4
5
6
7
8
9
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False

==生成器一定是迭代器(反之不成立)==。但 listdictstr虽然是 Iterable,却不是 Iterator

listdictstrIterable 变成 Iterator 可以使用 iter() 函数:

1
2
3
4
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True

任何实现了 __iter____next__()(python2中实现 next())方法的对象都是迭代器,__iter__ 返回迭代器自身,__next__ 返回容器中的下一个值,如果容器中没有更多元素了,则抛出 StopIteration 异常,至于它们到底是如何实现的这并不重要。

为了更直观地感受迭代器内部的执行过程,我们自定义一个迭代器,以斐波那契数列为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Fib:
def __init__(self, number):
self.prev = 0
self.curr = 1
self.number = number
def __iter__(self):
return self

def __next__(self):
value = self.curr
if self.number <= 0:
raise StopIteration
self.curr += self.prev
self.prev = value
self.number -= 1
return value

>>> for i in Fib(6):
... print(i)
1
1
2
3
5
8

Fib既是一个可迭代对象(因为它实现了 __iter__ 方法),又是一个迭代器(因为实现了 __next__ 方法)。实例变量 prevcurr 用户维护迭代器内部的状态。每次调用 next() 方法的时候做两件事:

  1. 为下一次调用next()方法修改状态
  2. 为当前这次调用生成返回结果


  3. 迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。

    支持一下
    扫一扫,支持heaven
    • 微信扫一扫
    • 支付宝扫一扫