迭代器

学习过python的童鞋都应该知道python中有三大神器,即 迭代器、生成器和装饰器,所谓神器,必有其神奇之处,今天和大家一起学习一下迭代器和生成器。

迭代是python比较强大的功能之一,说起迭代,我们可能会想到,对于一个列表list,一个元组tuple,访问的时候都可以使用 for .. in .. ,没错,这就是迭代,它是访问集合元素的一种方式,在从头到尾遍历可迭代对象时,能够记住遍历的位置。

这里给大家区分两个概念 可迭代对象(iterable)和迭代器(iterator), 我们往往说某个对象是不是可迭代的,而迭代器可以抽象为一个‘器’,就是在从头到尾遍历可迭代对象中的某一元素时,能够记住遍历的位置的那玩意儿,二者之间有本质区别,下面举例说明一下

from collections import Iterable
test = ['a', 'b']
for i in test:
print(i)

# 输出结果,说明列表是可迭代的

a
b
isinstance([], Iterable)
True
for i in 10:
print(i)

#输出结果,说明整型数字是不可迭代的

TypeError: 'int' object is not iterable
isinstance(10, Iterable)
False

上面我们看到的是可迭代对象的外在表现,那么可迭代对象的本质又是什么呢?答案就是一个具备__iter__方法的对象就是可迭代对象,话不多说,上代码看一下

from collections import Iterable
class MyIter():
def __init__(self):
pass
def __iter__(self):
pass
if __name__ == "__main__":
test = MyIter()
print(isinstance(test, Iterable))

输出结果

True

上面说过迭代器能够记住可迭代对象属性的位置,其本质是在可迭代对象中加了__next__()方法(python2中是next()方法),下面手动实现一个迭代器:

class MyIterator():
def __init__(self):
self.test_list = ['Hello', 'I', 'Love', 'python']
self.position = 0
def __iter__(self):

# 要返回一个迭代器,这里返回自身

return self
def __next__(self):
# 该方法的主要作用记录迭代输出位置
if self.position < len(self.test_list):
ret = self.test_list[self.position]
self.position += 1
return ret

# 当超出列表长度的时候,抛出停止迭代的异常

else:
raise StopIteration
if __name__ == "__main__":
test = MyIterator()
for i in test:
print(i, end=' ')

# 输出结果

Hello I Love python

总结一下,判断一个对象是不是可迭代的,就看该对象有没有实现__iter__()方法,要判断一个对象是不是迭代器,就看该对象有没有实现__iter__()方法和__next__()方法。我们平时用的比较多的for .. in ..循环,其执行过程的本质就是通过__iter__()获取一个可迭代对象,然后通过该调用对象的__next__()方法获取下一个值,知道满足停止迭代的条件之后,抛出停止迭代的异常,然后停止循环。

生成器

生成器(generator)是一种的特殊的迭代器,其特殊之处在于我们可以根据需要生成数据,生成器的创建方式有两种,第一种比较简单,将列表推导式的[]换成()就是生成器.

In [3]: a = [x**2 for x in range(5)]
In [4]: a
Out[4]: [0, 1, 4, 9, 16]
In [5]: b = (x**2 for x in range(5))
In [6]: b # b是一个生成器对象
Out[6]: at 0x000001E45FF01B88>
# 生成器具有__next__()方法
In [7]: b.__next__()
Out[7]: 0
In [8]: b.__next__()
Out[8]: 1

第二种方式就是通过使用yield创建生成器,一个函数,只要里面含有yield,那么它一定就是生成器,下面写个简单的代码说明一下通过yield创建的生成器。

def my_generator():
i = 0
print("1111111")
while i<5:
yield i
i += 1
print("22222")
print("33333")
if __name__ == "__main__":
gg = my_generator()
print(gg)
# 输出 
print(gg.__next__())
# 1111111
# 0
print(gg.__next__())
# 22222
# 1
print(gg.__next__())
# 22222
# 2

简述一下上面代码执行的流程:函数执行到第一步(gg=my_generator())的时候,python编译器发现函数中有yield,将这个函数定义为一个生成器,于是实例化gg为一个生成器对象,上面说过,生成器是一个特殊的迭代器,通过第一次调用__next__()方法,函数从上往下执行,当执行到yield的时候,将yield 后的对象(0)返回,当第二次调用__next__()方法的时候,会接着yield i继续往下执行,所以先打印22222,执行到yield的时候,将yield 后的对象(1)返回,以此类推。

讲到yield,总避免不了一个话题,那就是yield与return的对比,二者都能结束函数的运行,他们的区别主要是return 之后,函数下次执行只能从头开始执行,而yield 能够接着继续执行,这就是二者主要区别。