关系

首先从概念上来说,生成器都是迭代器,迭代器都是可迭代对象。

从另一个角度,可迭代对象包含迭代器、序列(字符串、列表和元组)以及字典。

如果一个对象拥有__iter__方法,其就是可迭代对象;再次基础上添加__next__方法,就是迭代器。

可迭代对象

如何自定义可迭代对象?

首先定义一个普通类,利用iter()方法调用,看会出现什么情况?

1
2
3
4
5
6
class MyIterable:
def __str__(self):
return "example class"

ex = MyIterable()
print(iter(ex))

此时运行会输出TypeError: 'MyIterable' object is not iterable,可知MyIterable是不可迭代的。

在上面的基础上添加__iter__方法

1
2
3
4
5
class MyIterable:
def __str__(self):
return "example class"
def __iter__(self):
return iter([1,2,3])

再次运行输出内容为<list_iterator object at 0x7f54747f6f70>,说明iter(ex)是一个list_iterator。

这里说一下__iter__的返回值,__iter__的返回值应该是一个迭代器,在实现上有两种方式:

  1. 若当前对象是一个迭代器,可以直接返回self
  2. 另一种情况是返回其他的一种迭代器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class MyIterable:
def __str__(self):
self.num = 0
return "example class"

# 第一种实现
def __iter__(self):
return self

def __next__(self):
print("__next__")
self.num += 1
return self.num

# 第二种实现
def __iter__(self):
return iter([1,2,3])

迭代器

迭代器是一种特殊可迭代对象,可以在可迭代对象的基础上添加__next__方法,可以使用内置函数next()来获取下一个元素,当迭代完所有元素后,不可再次迭代,如果继续获取下一个元素,则抛出StopIteration异常。

可以通过调用next()函数判断对象是否为迭代器,如果可以使用next()函数获取元素的对象,则一定为迭代器。

许多函数都能返回迭代器,例如enumerate

对于可迭代对象转为迭代器的方法主要有两种,一种就是通过添加__next__方法实现,另一种就是利用iter()函数将列表、元组等转为迭代器。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 自定义迭代器
class MyIterable:
def __str__(self):
self.num = 0
return "example class"

def __iter__(self):
return self

def __next__(self):
print("__next__")
self.num += 1
return self.num

生成器

生成器是一种特殊的迭代器,但是不需要手动写__iter____next__方法。

获取生成器对象有两种方法,一种是生成器表达式,另一种是生成器函数。

生成器表达式

将列表推导式的中括号改为小括号,它返回的是一个生成器对象而不是列表对象。

1
example = (i for i in range(i))

生成器函数

对于生成器函数,是在函数体中没有return关键字,而是采用yield返回值。

1
2
3
4
5
def foo():
i = 0
if i < 10:
yield i
i += 1

yield

当函数体中存在yield关键字时,便认为该函数为生成器函数,可以用过调用next函数获取值。
当生成器遇到yield时,便会暂停运行生成器,返回yield后面的值,保存当前状态,当再次调用生成器时,会从刚才暂停的地方继续执行,知道下一个yield

send

可以通过send方法向函数体中传值,但第一必须有None进行初始化。

1
2
3
4
5
6
7
8
def foo():
value = yield 1
yield value

exam = foo()

print(exam.send(None))
print(exam.send(11))

输出为

1
2
1
11

通过exam.send(None)初始化生成器,程序运行至yield 1时,返回1;之后再用生成器的send方法进入生成器内部,同时代入11,赋值给value。直到运行至yield value,将value值返回。