”迭代器“其实目的也是为了”循环“,更严谨一些,是为了“遍历”,你可以把迭代器看成比普通循环更高级别的工具,普通循环能搞定的迭代器也能搞定,普通循环搞不定的迭代器还能搞定,并且使用迭代器比普通循环效率更高。
大家先要知道“协议”(protocol)的意思,其实协议是用来“规范/标准化”你“创造的东西”的。比如,你开天辟地的发明了一种东西叫做“吧啦哔哩”,你给小明说:“小明,给我发一个吧啦哔哩过来”,如果小明不知道啥叫“吧啦哔哩”,那么小明会直接懵逼的。这时候你就要定一个“协议”如下:
1, "吧啦哔哩"一共有10个字
2, "吧啦哔哩"开头和结尾都是"#"号 (占两个字)
3, "吧啦哔哩"最后四位是"blbl"
4, 其他随便
那么我们根据这个协议,可以很轻易的构造出“吧啦哔哩”来:#1234blbl# 或者 #8888blbl#
同样,我们根据这份协议,就可以用来检测你得到的是不是“吧啦哔哩”,#1234blbl# -> 是,#1234blbl!-> 不是
明白了上面的东西,下面我们就开始“迭代”之旅,迭代顾名思义,就是重复的的既定的任务,直到完成。所以,为了完成迭代,我们需要一个迭代器!那么什么是迭代器呢?来看看迭代器的协议吧
迭代器协议 iterator protocol
从前有个人发明了迭代器,为了让大家明白什么是迭代器,他就写了这个协议,那么协议的内容简而言之就是一句话:如果一个对象包括一个叫"next"(python3 为__next__)的方法,那么这个对象就叫做“迭代器”。
好了,那么我们根据这个协议可以创建一个迭代器(iterator)
class Counter:
def __init__(self):
self.index = 0
def __next__(self):
i = self.index
if i < 10:
self.index += 1
return i
这个Counter就是一个迭代器,但是目前它没有什么太大的作用,因为我们不可能每次通过手动调用__next__方法来进行操作。
好消息是,很多编程软件为我们提供了一个“语法糖”(syntactic sugar),让这个语法糖来替我们反复执行__next__方法,比如python中的"for.. in",但是,为了让这个反复执行的过程停下来,我们同样需要定义一个终止信号,在python中,终止信号就是抛出一个StopIteration的“例外”(exception),来告知我们的语法糖:”好啦,没东西可以迭代了,可以停了“,这样迭代就终止了。
所以我们再进一步规范一下我们创建的迭代器成如下形式:
class Counter:
def __init__(self):
self.index = 0
def __next__(self):
i = self.index
if i < 10:
self.index += 1
return i
else:
raise StopIteration
好了,我们来试一下:
counter = Counter()
for i in counter:
print(i)
不妙,报错了。。
TypeError: 'Counter' object is not iterable
错误显示说:这个Counter对象不是可迭代的!这是什么意思呢?
原来,为了使用这个for..in 迭代语法糖,我们需要在in后面放可以迭代的“迭代器”,什么是可以迭代?你可以认为就是可以使用for..in语法糖,让语法糖帮你重复调用next方法就好了。如果不可以迭代,
那么for..in这个语法糖就无法为我们自动调用next方法。
所以说,为了使用for..in语法糖来进行迭代我们的迭代器,你必须让你的迭代器可迭代(有点绕。。哈哈)。
这句话有两层含义:
1,为了使用for..in语法糖,你必须让你的迭代器可迭代
2,你如果不适用for..in语法糖,你就不必让你的迭代器可迭代,你可以自己写一个语法糖,不断地调用next方法,当遇到StopIteration例外的时候停止罢了。
但是当你使用别人(编程语言)实现编写好的语法糖时,你就必须按照他们的规则走。
好了,我们现在明白了,通常来讲,当我们要创建了一个迭代器时,我们还“必须”(注意是必须)让迭代器可迭代,这样理解:因为一个不可迭代的迭代器是没有意义的!
所以,注意!从现在开始到文章结束,我所说的“迭代器”都是“可迭代”的迭代器!
那么怎么让我的迭代器可迭代呢?同样,来看什么是“可迭代协议”(iterable protocol)
可迭代协议 iterable protocol
在python中,为了使一个”对象“可迭代:
1,这个迭代器必须同时包含另一个方法叫做“__iter__”
2,这个"__iter__"方法还得返回一个”迭代器“(可迭代)
请注意,上面我说的是:为了使一个”对象“可迭代,这里,对象可以指我们刚刚创建的”Counter“迭代器,也可以是其他的对象。
来个栗子:
为了使我们刚才创建的Counter迭代器对象“可迭代”,那么:
1,我们就在这个Counter对象里面添加一个叫__iter__的方法 (可迭代化操作)
2, 让这个__iter__方法返回一个“可迭代的迭代器” (这里就是自己了!)
class Counter:
def __init__(self):
self.index = 0
def __iter__(self):
return self
def __next__(self):
i = self.index
if i < 10:
self.index += 1
return i
else:
raise StopIteration
counter = Counter()
for i in counter:
print(i)
Cool! 这个时候我们得到了0,1,2,3,4,5,6,7,8,9的迭代!
这里简单说一些执行步骤,当我们使用for..in语法糖的时候,它先调用__iter__方法,得到返回的迭代器,然后连续调用该迭代器的__next__方法,知道遇到StopIteration例外
我上面也提到了,我们不仅可以使迭代器“可迭代”,我们也可以使普通的对象“可迭代”,只需给该对象添加一个__iter__的方法,然后返回一个可迭代的迭代器就好了!
这里顺便插一句!在python中,我们可以使用"iter"这个函数来返回一个“可迭代的迭代器”。
比如:
x = iter([1, 2, 3])
print(x) #<list_iterator object at 0x10c828550>
x.__next__() # 返回 1
x.__next__() # 返回 2
x.__next__() # 返回 3
x.__next__() # 返回 StopIteration
所以,我们可以让一个普通对象可迭代,而不一定非得是迭代器。
class Name:
def __iter__(self):
return iter(['zhangsan', 'lisi', 'wangwu'])
name = Name()
for n in name:
print(n)
不错!我们得到了zhangsan, lisi, wangwu
现在逻辑不是很复杂的情况之下,这种创建迭代器的方式还是能够接受的,但是如果逻辑复杂,以及用这种模式多了,每次这么定义就不是很方便,于是为了“简化”创建迭代器的过程,“生成器”generator就出现了。