在python中有几个概念是经常用到的,可迭代对象(Iterable)、迭代器(Iterator)和生成器(Generator)。

可迭代对象(Iterable)

简单的说,一个对象只要实现了__iter__()方法,那么它就是可迭代对象。

1
2
3
class IterObj:
def __iter__(self):
return self

可以使用isinstance()函数检查是否是可迭代对象

1
2
3
4
from collections.abc import Iterable

it = IterObj()
print(isinstance(it, Iterable)) # true

常见的可迭代对象

在python中,有一些常见的可迭代对象

  1. list、tuple、set、dict、str 这5种对象
  2. 文件对象
  3. 定义了__iter__()方法的对象

验证一下第1,2点:

1
2
3
4
5
6
7
8
9
10
11
import os

print(isinstance([], Iterable)) # true list 是可迭代的
print(isinstance({}, Iterable)) # true 字典是可迭代的
print(isinstance((), Iterable)) # true 元组是可迭代的
print(isinstance(set(), Iterable)) # true set是可迭代的
print(isinstance('', Iterable)) # true 字符串是可迭代的

currPath = os.path.dirname(os.path.abspath(__file__))
with open(currPath+'/model.py') as file:
print(isinstance(file, Iterable)) # true 文件对象是可迭代的

iter()方法

包含__iter__方法的可以使用内置的iter()函数调用并转化成Iterator对象

1
2
3
4
5
6
class IterObj:
def __iter__(self):
return self

it = IterObj()
print(iter(it))

这里使用iter()函数将 IterObj 的实例转换一下:

1
2
3
4
Traceback (most recent call last):
File "<ipython-input-5-1e3a4bca5dfa>", line 7, in <module>
print(iter(it))
TypeError: iter() returned non-iterator of type 'IterObj'

出现了类型错误,iter()你们将 “非迭代器”类型转化为迭代器。

要解决这个错误据需要修改一下IterObj,__iter__()方法返回一个Iterator对象,我们可以借用内置的可迭代对象来实现:

1
2
3
4
5
6
7
8
class IterObj:
a = [3, 5, 7, 11, 13, 17, 19]

def __iter__(self):
return iter(self.a)

it = IterObj()
print(iter(it)) # <list_iterator at 0x110831390>

因此在定义一个可迭代对象时,要非常注意__iter__()方法的内部实现逻辑,一般情况下,是通过一些已知的可迭代对象(例如,上文提到的集合、序列、文件等或其他正确定义的可迭代对象)来辅助实现。

迭代器(Iterator)

1
2
3
4
5
6
7
from collections.abc import Iterator
class IterObj:
def __iter__(self):
return self

it = IterObj()
print(isinstance(it, Iterator)) # false

可以看出它不是一个迭代器。

一个对象实现了iter()和next()方法,那么它就是一个迭代器对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from collections.abc import Iterator

class IterObj:
def __init__(self):
self.a = [1, 2, 3, 4, 5]
self.len = len(self.a) # 长度
self.i = 0 # 初始化索引指针值

def __iter__(self):
return iter(self.a)

def __next__(self):
if self.i < self.len:
tmp = self.a[self.i]
self.i += 1
return tmp
else:
self.i = 0
raise StopIteration()

it = IterObj()
print(isinstance(it, Iterator)) # true

上文提到的列表集合等对象是可迭代的但不是迭代器

1
2
3
4
5
print(isinstance([], Iterator)) # false
print(isinstance({}, Iterator)) # false
print(isinstance((), Iterator)) # false
print(isinstance(set(), Iterator)) # false
print(isinstance('', Iterator)) # false

而文件对象是迭代器

1
2
3
currPath = os.path.dirname(os.path.abspath(__file__))
with open(currPath+'/model.py') as file:
print(isinstance(file, Iterator)) # true 文件对象是迭代器

迭代器可以使用next()函数进行调用

1
2
3
it = IterObj()
next(it) # 1
next(it) # 2

生成器(Generator)

生成器既是可迭代的也是迭代器

定义生成器有两种方式:

  1. 列表生成器
  2. 使用yield定义生成器函数

第1种

1
2
3
4
5
6
7
from collections.abc import Iterable, Iterator, Generator

g = (i for i in range(10))
print(g) # <generator object <genexpr> at 0x110829ca8>
print(isinstance(g, Iterable)) # true
print(isinstance(g, Iterator)) # true
print(isinstance(g, Generator)) # true 是一个生成器

列表生成器可以不需要消耗大量的内存来生成一个巨大的列表,只有在需要数据的时候才会进行计算。

第2种情况

1
2
3
4
5
6
def gen():
for i in range(10):
yield i
g = gen()
print(next(g)) # 0
print(next(g)) # 1

这里 yield 可以简单理解为 return,这个函数就是顺序返回0到10之间的自然数,可以通过next()函数或for循环来遍历。

当程序遇到yield关键字时,这个生成器函数就返回了,直到再次执行了next()函数,它就会从上次函数返回的执行点继续执行。

在Python中利用生成器的这些特点可以实现协程,下面举一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def consumer():
res = ''
while True:
n = yield res
if not n:
return
print('consumer {} '.format(n))
res = 'ok'

def producer(c, data):
print('producer {}'.format(data))
res = c.send(data)
print('consumer return {}'.format(res))


if __name__ == '__main__':
c = consumer()
next(c) # 启动consumer
producer(c, 'hello')

可以看到返回

1
2
3
producer hello
consumer hello
consumer return ok