Python之迭代器

>可迭代对象和迭代器傻傻分不清

之前自己一直没搞明白可迭代对象和迭代器的关系,总觉得两个没有区别,这次再来一探究竟

先来理解个概念

可迭代对象(iterable)是可以迭代的任何东西,迭代器(iterator)是执行实际迭代的东西

>可迭代对象

让我们从熟悉的数据类型说起吧

In [1]: iter([1,2,3])
Out[1]: <list_iterator at 0x7fd6a01650f0>

In [2]: iter((1,2,3))
Out[2]: <tuple_iterator at 0x7fd6a0b57860>

In [3]: iter("hello word")
Out[3]: <str_iterator at 0x7fd6a0b57be0>

In [5]: iter(1)
TypeError: 'int' object is not iterable

看Out输出的内容,TypeError: 'int' object is not iterable,这句话的意思就是int类型不是一个可迭代对象,那为何上面的list/tuple/str没有报错呢?难道它们就是可迭代对象?这三种数据类型经过iter()方法后返回iterator迭代器?为什么呢...

为了验证我们的猜想,使用isinstance()来做个了结

In [17]: from collections import Iterable

In [19]: isinstance([1,2,3], Iterable)
Out[19]: True

In [20]: isinstance((1,2,3), Iterable)
Out[20]: True

In [21]: isinstance("hello word", Iterable)
Out[21]: True

In [22]: isinstance(1, Iterable)
Out[22]: False

经过我们的验证,list/tuple/str 是可迭代对象,int就不是,那list/tuple/str和int有什么区别呢?

细心的你发现,可迭代对象都是可以用for循环遍历的?而int就不可以,真的是这样吗?for循环和可迭代对象有关系吗?不急,我们再来验证下...

In [23]: for i in [1,2,3]:
    ...:     print(i)
    ...:
1
2
3

In [24]: for i in (1,2,3):
    ...:     print(i)
    ...:
1
2
3

In [25]: for i in "hello":
    ...:     print(i)
    ...:
h
e
l
l
o

In [27]: for i in 1:
    ...:     print(i)
    ...:
TypeError: 'int' object is not iterable

过不奇然,和我们猜想的一致,当我们对1使用for循环时,报出错误TypeError: 'int' object is not iterable,其它的就没有,难道for循环的条件也是需要一个可迭代对象?

废话这么多,那什么是可迭代对象呢?下面可要画重点了数据类型实现了iter方法的就是一个可迭代对象,那我们顺着这个线索去验证下,不能你说对就对,一起来看看吧!

In [29]: class Mylist(object):
    ...:     def __init__(self):
    ...:         self.container = []
    ...:     def add(self, item):
    ...:         self.container.append(item)
    ...:

In [30]: mylist = Mylist()

In [31]: mylist.add(1)

In [32]: mylist.add(2)

In [33]: for i in mylist:
    ...:     print(i)
    ...:
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-33-082208dcc545> in <module>
----> 1 for i in mylist:
      2     print(i)
      3
TypeError: 'Mylist' object is not iterable

有结论吗?不急不急,再来看一个

In [35]: class Mylist(object):
    ...:     def __init__(self):
    ...:         self.container = []
    ...:     def add(self, item):
    ...:         self.container.append(item)
    ...:     def __iter__(self):
    ...:         pass
    ...:
In [36]: mylist = Mylist()

In [37]: mylist.add(1)

In [38]: mylist.add(2)

In [39]: for i in mylist:
    ...:     print(i)
    ...:
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-39-082208dcc545> in <module>
----> 1 for i in mylist:
      2     print(i)
      3
TypeError: iter() returned non-iterator of type 'NoneType'

In [40]: from collections import Iterable

In [41]: isinstance(mylist, Iterable)
Out[41]: True

仔细的观众已经看出了猫腻,一个数据类型是否是可迭代对象关系到它是否实现了iter方法,至此,关于可迭代对象已经说明白了

那迭代器呢?细心的观众又发现了,在我们用for循环遍历mylist时,报出了错误TypeError: iter() returned non-iterator of type 'NoneType',到此,我们可以知道,for循环遍历的过程中,调用了iter()方法报出了return non-iterator,说iter()返回的不是一个迭代器,龟龟,这就把迭代器引出来了,既然出来了,那就看看吧!

我们在Mylist类中,实现了iter方法,但是没有具体的实现,当我们使用for循环时,并没有报出'Mylist' object is not iterable的错误,那我们就可以推断Mylist是一个可迭代对象,但接下来却报出了return non-iterator,说明for循环遍历时需要一个迭代器,而这个迭代器是iter()方法返回的,至此,我们可以得到一个结论,迭代器是可迭代对象;那什么又是迭代器呢?

我们将目光转移到文章的开始

In [1]: iter([1,2,3])
Out[1]: <list_iterator at 0x7fd6a01650f0>

In [2]: iter((1,2,3))
Out[2]: <tuple_iterator at 0x7fd6a0b57860>

In [3]: iter("hello word")
Out[3]: <str_iterator at 0x7fd6a0b57be0>

In [5]: iter(1)
TypeError: 'int' object is not iterable

我们对list/tuple/str数据类型使用iter方法时,返回的结果时**_iterator,这是一个迭代器,我们对比Mylist这个类,它是一个可跌代对象,iter()返回的却不是一个迭代器,那这个迭代器就是这个iter()方法返回的?那iter()内部需要怎么实现呢?

In [2]: from collections import Iterator

In [3]: isinstance([1,2,3], Iterator)
Out[3]: False

In [4]: isinstance(iter([1,2,3]), Iterator)
Out[4]: True

In [5]: isinstance("hello", Iterator)
Out[5]: False

In [6]: isinstance(iter("hello"), Iterator)
Out[6]: True

接下来再看个例子

In [22]: it = iter("hello")

In [23]: next(it)
Out[23]: 'h'

In [24]: next(it)
Out[24]: 'e'

In [25]: next(it)
Out[25]: 'l'

In [26]: next(it)
Out[26]: 'l'

In [27]: next(it)
Out[27]: 'o'

In [28]: next(it)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-28-bc1ab118995a> in <module>
----> 1 next(it)

StopIteration:

每次调用next()都返回一个值,这个和for的遍历是不是相同呢?还记得我们在Mylist类中使用for遍历它的实例,返回TypeError: iter() returned non-iterator of type 'NoneType',说iter()返回的不是迭代器。再结合上面和for循环对字符串的遍历,是否next()就是实现迭代器的关键呢?那就来试试

In [38]: class Mylist(object):
    ...:     def __init__(self):
    ...:         self.container = []
    ...:     def add(self, item):
    ...:         self.container.append(item)
    ...:     def __iter__(self):
    ...:        myiterator =  MyIterator(self)
    ...:        return myiterator
    ...:
In [39]: class MyIterator(object):
    ...:     def __init__(self,mylist):
    ...:         self.mylist = mylist
    ...:         self.current = 0
    ...:     def __next__(self):
    ...:         if self.current < len(self.mylist.container):
    ...:             item = self.mylist.container[self.current]
    ...:             self.current += 1
    ...:             return item
    ...:         else:
    ...:             raise StopIteration
    ...:     def __iter__(self):
    ...:         return self
    ...:
In [40]: mylist = Mylist()

In [41]: mylist.add(1)

In [42]: mylist.add(2)

In [43]: mylist.add(3)

In [44]: mylist.add(4)

In [45]: mylist.add(5)

In [46]: for i in mylist:
    ...:     print(i)
    ...:
1
2
3
4
5

In [47]:

我们发现可以使用for循环遍历数据了,我们来做一个斐波那契数列的迭代器

In [54]: class FibIterator(object):
   ...:     """斐波那契数列迭代器"""
   ...:     def __init__(self, n):
   ...:         """
   ...:         :param n: int, 指明生成数列的前n个数
   ...:         """
   ...:         self.n = n
   ...:         # current用来保存当前生成到数列中的第几个数了
   ...:         self.current = 0
   ...:         # num1用来保存前前一个数,初始值为数列中的第一个数0
   ...:         self.num1 = 0
   ...:         # num2用来保存前一个数,初始值为数列中的第二个数1
   ...:         self.num2 = 1
   ...:
   ...:     def __next__(self):
   ...:         """被next()函数调用来获取下一个数"""
   ...:         if self.current < self.n:
   ...:             num = self.num1
   ...:             self.num1, self.num2 = self.num2, self.num1+self.num2
   ...:             self.current += 1
   ...:             return num
   ...:         else:
   ...:             raise StopIteration
   ...:
   ...:     def __iter__(self):
   ...:         """迭代器的__iter__返回自身即可"""
   ...:         return self
   ...:

In [55]: fb = FibIterator(10)

In [56]: next(fb)
Out[56]: 0

In [57]: next(fb)
Out[57]: 1

In [58]: next(fb)
Out[58]: 1

In [59]: for i in fb:
   ...:     print(i)
   ...:
2
3
5
8
13
21
34

至此,我们来总结下可迭代对象和迭代器

实现了iter()方法的就是可迭代对象;实现了Iter()和next()方法的就是迭代器

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容

  • 一、总体内容 1.1、协程的介绍 1.2、迭代器以及迭代器的应用 1.3、生成器(生成器与迭代器保存的都是生成数据...
    IIronMan阅读 861评论 0 1
  • 当容器中的元素很多的时候,不可能全部读取到内存,那么就需要一种算法来推算下一个元素,这样就不必创建很大的容器,生成...
    编程自习室阅读 546评论 0 1
  • 迭代器 什么是迭代器 迭代器是指用 iter(可迭代对象) 函数返回的对象(实例) 迭代器可以用 next(it)...
    遇明不散阅读 194评论 0 0
  • 更深入理解 Python 中的迭代 深入探讨 Python 的 for 循环来看看它们在底层如何工作,以及为什么它...
    lvyz0207阅读 2,497评论 3 10
  • 文/铁骨柔情 下雨的早上,搭乘朋友家的车,送孩子上学。快到学校门口时,坐在副驾驶位置的我,习惯性的透过车窗向...
    铁骨柔情青岛阅读 218评论 0 0