您的位置:首页 > 编程语言 > Python开发

深入理解Python迭代器和可迭代对象

2018-02-08 11:11 866 查看

1 迭代器

迭代是指对集合元素遍历的一种方式,迭代器是可以实现对集合从前向后依次遍历的一个对象


2 可迭代对象

定义(表面理解)

表面来看,只要可以用 for...in...进行遍历的对象就是可迭代对象


自定义可迭代对象(本质)

语法层面,如果一个对象实现了__iter__方法,那么这个对象就是可迭代对象


判断是否是可迭代对象(Iterable)

通过调用Python内置的isinstance来判断是否是Iterable的实例

In [9]: isinstance([],Iterable)
Out[9]: True

In [10]: isinstance('',Iterable)
Out[10]: True

In [11]: isinstance({},Iterable)
Out[11]: True

In [12]: class MyList(object):
....:     def __init__(self):
....:         self.list = []
....:     def add(self,item):
....:         self.list.append(item)
....:

In [13]: mylist = MyList()

In [14]: isinstance(mylist,Iterable)
Out[14]: False


通过上面例子可以看出,Python中的内置类型dict,list,str的对象都是可迭代的,我们自定义了一个类MyList,由于这个类没有实现iter方法,所以这个类的实例不是可迭代对象

接下来实现一个自定义的可迭代对象,只需要给MyList类添加一个iter方法就好。

In [16]: class MyList(object):
....:     def __init__(self):
....:         self.list = []
....:     def add(self,item):
....:         self.list.append(item)
....:     def __iter__(self):
....:         pass
....:

In [17]: from collections import Iterable

In [18]: mylist = MyList()

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


至此,我们已经实现了一个自定义的可迭代对象,但是这个可迭代对象还不能通过for…in…语法去迭代,因为我们还缺一个迭代器去帮我们遍历。for…in…遍历的本质就是调用对象的iter方法返回一个迭代器,然后通过这个迭代器去依次取得可迭代对象的每一个元素。Python中的内置可迭代对象用到的迭代器Python已经帮我们实现了,我们自定义的可迭代对象的迭代器只有我们自己动手实现了。

给自定义可迭代对象插上迭代器翅膀

1 from collections import Iterable
2
3 class MyList(object):
4     def __init__(self):
5         self.items = []
6
7     def add(self,val):
8         self.items.append(val)
9
10    def __iter__(self):
11
12        myiterator = MyIterator(self)
13        return myiterator
14
15 class MyIterator(object):
16
17     def __init__(self,mylist):
18         self.mylist = mylist
19         self.current= 0
20
21     def __next__(self):
22         if self.current < len(self.mylist.items):
23             item = self.mylist.items[self.current]
24             self.current += 1
25             return item
26         else:
27             raise StopIteration
28     def __iter__(self):
29         return self
30
31 if __name__ == '__main__':
34     mylist = MyList()
35     mylist.add(1)
36     mylist.add(2)
37     mylist.add(3)
38     mylist.add(4)
39     mylist.add(5)
40     for num in mylist:
41         print(num)


for…in…遍历mylist时,先通过mylist的iter方法拿到一个迭代器对象myiterator,然后不断调用myiterator的next的方法依次取到遍历的每一个元素。也许会困惑的一点是” MyIterator中的iter方法有什么卵用 ? 里面为什么还要return self, 毕竟我们只需要迭代器的next方法啊!”。其实上面这种写法,完全可以不实现iter这个方法,但是上面例子这种写法在实际项目中是很少见的,通常我们创建一个自定义的可迭代对象不会写两个类这么臃肿,而是写一个类,让这个类既是可迭代对象又是迭代器,此时iter方法需要返回一个迭代器,而自身又是一个迭代器,所以只需要返回自身即可。

将上面两个类优化成一个类

1 class MyList(object):
2
3     def __init__(self):
4         self.items = []
5         self.current = 0
6
7     def add(self,val):
8         self.items.append(val)
9
10    def __next__(self):
11        if self.current < len(self.items):
12            item = self.items[self.current]
13            self.current += 1
14            return item
15        else:
16            raise StopIteration
17
18    def __iter__(self):
19        return self   #可迭代对象本身就是一个迭代器
20
21 if __name__ == '__main__':
22     mylist = MyList()
23     mylist.add(1)
24     mylist.add(2)
25     mylist.add(3)
26     mylist.add(4)
27     mylist.add(5)
28     for num in mylist:
29         print(num)


3. 迭代器(可迭代对象)的应用

因为通常可迭代对象本身就是一个迭代器,所以二者的用途一般是一致的。我们发现迭代器的本质就是通过next()函数(next本质是调用迭代器的_next方法)不断得到下一个数据,假如需要遍历的数据是可以通过计算规则依次得到的,这时候通过迭代器(升级版本是生成器)去遍历就会避免传统遍历需要依赖已有数据集合从而需要占用大量内存的弊端。下面通过一个经典例子求斐波那契数列的前n项来说明迭代器的优势。

斐波那契数列

数列中第一个数为0,第二个数为1,其后的每一个数都是前两个数相加之和:

0,1,1,2,3,5,8,13,21,34...


通过迭代器实现输出前n项的代码:

1 #coding:utf-8
2 class FibIterator(object):
3     def __init__(self,n):
4         self.n = n       #需要遍历的元素个数
5         self.current = 0 #当前遍历的元素个数
6         self.num1 = 0 #第一个元素
7         self.num2 = 1 #第二个元素
8
9     def __next__(self):
10         if self.current < self.n:
11             self.num1, self.num2 = self.num2, self.num1+self.num2
12             self.current+=1
13             return self.num1
14         else:
15             raise StopIteration
16
17     def __iter__(self):
18         return self
19
20 if __name__ == '__main__':
21     fibiterator = FibIterator(10)
22     for num in fibiterator:
23         print(num)


可以看出遍历的元素是通过即时运算生成的,而没有事先占用任何存储空间。迭代器和可迭代对象的知识到这里差不多已经全介绍完了,下篇将会分析Python另一个比较重要的知识点生成器。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: