目录
- 1 迭代器
- 2 可迭代对象
- 3 生成器
1 迭代器
迭代器是一种可以更新迭代的工具,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。但是他不能像列表一样使用下标来获取数据,也就是说迭代器是不能返回的。迭代器只能往前不会后退。
专业解释是迭代器含有两个方法:
__next__
:返回下一个可用元素,如果没有元素,抛出StopIteration异常;
__iter__
:返回迭代器本身,以便在应该使用可迭代对象的地方使用迭代器,比如for循环中。
如下,实现一个斐波那契数列迭代器:
class Fib:
def __init__(self, stop):
self.stop = stop
self.a, self.b = 0, 1
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a + self.b
if self.a > self.stop:
raise StopIteration
return self.a
f = Fib(5)
print(f.__next__())
print(next(f))
print(next(f))
print(next(f))
print(next(f))
print(next(f))
# 也可以使用for循环
# for i in f:
# print(i)
使用迭代器的好处在于:
它是一种延迟操作,即当需要用到的时候才去产生结果。比如对于一个序列来说,如果我们要遍历它,并不需要再一开始就把所有元素都生成好,而是只需要知道每个元素的下一个元素是什么就可以了。这样可以节省很多空间,尤其对于数量很大的集合来说。
2 可迭代对象
如果一个对象定义了 __iter__
方法,那它就是一个可迭代对象。可迭代对象通常可以通过iter(x)来返回一个迭代器,可以被 for 循环使用。当解释器需要迭代对象x时,会自动调用iter(x)。
内置的iter函数有以下作用:
- 检查对象是否实现了
__iter__
方法,如果实现了就调用它,获得一个迭代器。 - 如果没有实现
__iter__
方法,但是实现了__getitem__
方法,python会创建一个迭代器,尝试按顺序(从索引0开始)获取元素。 - 如果尝试失败,python会抛出
TypeError
异常,通常会提示"C object is not iterable",其中C是目标对象所属的类。
检查一个对象能否迭代,最准确的方法是调用iter(x)函数,如果不可迭代,再处理TypeError异常。或者可以用collections模块的Iterable类型判断。
迭代器(Iterator)和可迭代(Iterable)这两个的差别:
- 一个迭代器一定是可迭代对象,因为它一定有
__iter__
方法。反过来则不成立。(事实上,Iterator 就是 Iterable 的子类) - 迭代器的
__iter__
方法返回的是自身,并不产生新实例。而可迭代对象的__iter__
方法通常会生成一个新的迭代器对象。
我们常见的可迭代对象有:
- 集合数据类型,如list、tuple、dict、set、str等;
- 生成器(generator),包括生成器和带yield的生成器函数(generator function)。后面介绍。
可迭代对象的for循环实现过程:
- 首先 for 循环会调用可迭代对象的
__iter__
方法,获取相应的迭代器 - 每次循环,将迭代器的
__next__
方法的返回值赋值给循环变量 - 直到捕获迭代器抛出的
StopIteration
异常,循环结束
比如对一个列表进行迭代时,
x = [1,2,3]
for i in x:
print(i)
实际执行情况如下图:
3 生成器
使用了 yield 的函数被称为生成器(generator)。生成器其实是一种特殊的迭代器,但是迭代器不一定是生成器。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
生成器来实现斐波那契数列:
def fib(stop):
n, a, b = 1, 0, 1
while n < stop:
yield b
a, b = b, a + b
n += 1
return "没有数据了"
f = fib(10) # f 是一个迭代器,由生成器返回生成
while True:
print(next(f), end=" ")
# 当迭代遇到没有元素可取,也会抛出StopIteration异常
生成器还有一个send方法,可以往生成器里的变量传值,如下代码:
def foo():
n = 0
while n < 5:
count = yield n
print(count)
n += 1
f = foo()
r1 = f.send(None)
print(r1)
r2 = f.send(1)
print(r2)
r3 = f.send(2)
print(r3)
r4 = f.send(3)
print(r4)
r5 = f.send(4)
print(r5)
f = foo()返回一个生成器,f.send(None)进入函数执行代码,遇到count=yield n,冻结并跳出函数体,当下次遇到f.send(1),再次进入函数体,并遇到ount=yield n停止,把send的值给了count,并且返回了n,冻结并跳出函数体,依次执行。
生成器表达式
生成器表达式是列表推导式的生成器版本,看起来像列表推导式,但是它返回的是一个生成器对象而不是列表对象。
a = (x for x in range(10))
print(a) # <generator object <genexpr> at 0x7ff1d0094c80>
总结:
- 迭代器含有
__iter__
和__next__
方法,其中__iter__
发会自身,一般用于for循环,__next__
获取下一个可用元素 - 可迭代对象定义了
__iter__
方法并返回一个迭代器,或实现了__getitem__
方法 - 生成器使用了 yield 的函数
- 可迭代对象可以生成迭代器,生成器是一种特殊的迭代器
- 注意迭代器和生成器使用时,不能重复使用,取到到无可用元素就会抛出StopIteration异常
参考:
https://zhuanlan.zhihu.com/p/55098524
https://www.cnblogs.com/eastonliu/p/9156418.html