python之路---迭代器和生成器
Posted 悍妻
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python之路---迭代器和生成器相关的知识,希望对你有一定的参考价值。
迭代器
初识迭代器:
迭代器的优点:节省内存
迭代器有两种:
一种是调用方法直接返回的
一种是可迭代(的)对象通过执行iter方法得到的(一些讲的就是这种)
能被for循环,就是可迭代的
可迭代的(iterable):
- str list tuple dict set range() 都可以被for循环,说明是可迭代的
from collections import Iterable l = [1, 2, 3, 4] t = (1, 2, 3, 4) d = {1: 2, 3: 4} s = {1, 2, 3, 4} print(isinstance(l, Iterable)) # True print(isinstance(t, Iterable)) # True print(isinstance(d, Iterable)) # True print(isinstance(s, Iterable)) # True
不能被for循环,就是不可迭代的
不可迭代的:
- int
for i in 1234 print(i) 结果: Traceback (most recent call last): File "test.py", line 4, in <module> for i in 1234: TypeError: ‘int‘ object is not iterable
那就有了一个问题,for循环怎么知道list,dict等是可迭代的,int就是不可迭代的呢?
答案就是满足的for的’要求‘的就是可迭代的,不满足就是不可迭代的。
这个“要求”叫做可迭代协议,就是看该数据类型内部是否实现了__iter__方法,如果该数据类型内部实现了__iter__方法,那就能for循环,能for循环,那就是可迭代的。
l = [1, 2, 3, 4] t = (1, 2, 3, 4) d = {1: 2, 3: 4} s = {1, 2, 3, 4}
print(dir(l)) #dir(l)是列表中实现的所有方法 print(dir(t)) #dir(t)是元组中实现的所有方法 print(dir(d)) #dir(d)是字典中实现的所有方法 print(dir(s)) #dir(s)是集合中实现的所有方法
[‘__add__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__delitem__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__gt__‘, ‘__hash__‘, ‘__iadd__‘, ‘__imul__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__mul__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__reversed__‘, ‘__rmul__‘, ‘__setattr__‘, ‘__setitem__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘append‘, ‘clear‘, ‘copy‘, ‘count‘, ‘extend‘, ‘index‘, ‘insert‘, ‘pop‘, ‘remove‘, ‘reverse‘, ‘sort‘] [‘__add__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__getnewargs__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__mul__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__rmul__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘count‘, ‘index‘] [‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__delitem__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__getitem__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__iter__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__setitem__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘clear‘, ‘copy‘, ‘fromkeys‘, ‘get‘, ‘items‘, ‘keys‘, ‘pop‘, ‘popitem‘, ‘setdefault‘, ‘update‘, ‘values‘] [‘__and__‘, ‘__class__‘, ‘__contains__‘, ‘__delattr__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__iand__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__ior__‘, ‘__isub__‘, ‘__iter__‘, ‘__ixor__‘, ‘__le__‘, ‘__len__‘, ‘__lt__‘, ‘__ne__‘, ‘__new__‘, ‘__or__‘, ‘__rand__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__ror__‘, ‘__rsub__‘, ‘__rxor__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__sub__‘, ‘__subclasshook__‘, ‘__xor__‘, ‘add‘, ‘clear‘, ‘copy‘, ‘difference‘, ‘difference_update‘, ‘discard‘, ‘intersection‘, ‘intersection_update‘, ‘isdisjoint‘, ‘issubset‘, ‘issuperset‘, ‘pop‘, ‘remove‘, ‘symmetric_difference‘, ‘symmetric_difference_update‘, ‘union‘, ‘update‘]
总结一下我们现在所知道的:可以被for循环的都是可迭代的,要想可迭代,内部必须有一个__iter__方法。
接着分析,__iter__方法做了什么事情呢?(为什么内部有__iter__方法就是可迭代的)
print([1,2].__iter__()) 结果 <list_iterator object at 0x1024784a8>
执行了list([1,2])的__iter__方法,我们好像得到了一个list_iterator。
所以 [1,2].__iter__() 是一个列表迭代器(list_iterator)
‘‘‘ dir([1,2].__iter__())是列表迭代器中实现的所有方法,dir([1,2])是列表中实现的所有方法,都是以列表的形式返回给我们的,为了看的更清楚,我们分别把他们转换成集合, 然后取差集。 ‘‘‘ #print(dir([1,2].__iter__())) #print(dir([1,2])) print(set(dir([1,2].__iter__()))-set(dir([1,2]))) 结果: {‘__length_hint__‘, ‘__next__‘, ‘__setstate__‘}
我们看到在列表迭代器中多了三个方法,那么这三个方法都分别做了什么事呢?
iter_l = [1,2,3,4,5,6].__iter__() #获取迭代器中元素的长度 print(iter_l.__length_hint__()) #根据索引值指定从哪里开始迭代 print(iter_l.__setstate__(4)) #一个一个的取值 print(iter_l.__next__()) print(iter_l.__next__())
打印结果:
6
None
5
6
这三个方法中,能让我们一个一个取值的神奇方法是谁?
没错!就是__next__
在for循环中,就是在(数据类型调用__iter__方法后生成的迭代器)内部调用了__next__方法才能取到一个一个的值。
l = [1,2,3,4] l_iter = l.__iter__() item = l_iter.__next__() print(item) # 1 item = l_iter.__next__() print(item) # 2 item = l_iter.__next__() print(item) # 3 item = l_iter.__next__() print(item) # 4 item = l_iter.__next__() print(item) # 抛出一个异常StopIteration
这是一段会报错的代码,如果我们一直取next取到迭代器里已经没有元素了,就会抛出一个异常StopIteration,告诉我们,列表中已经没有有效的元素了。
这个时候,我们就要使用异常处理机制来把这个异常处理掉。
l = [1,2,3,4] l_iter = l.__iter__() while True: try: item = l_iter.__next__() print(item) except StopIteration: break
那现在我们就使用while循环实现了原本for循环做的事情,我们是从谁那儿获取一个一个的值呀?是不是就是l_iter?好了,这个l_iter就是一个迭代器。
迭代器遵循迭代器协议:必须拥有__iter__方法和__next__方法。
from collections import Iterator print(isinstance(range(100000000),Iterator)) #验证range执行之后得到的结果不是一个迭代器
可迭代的对象调用__iter__方法后就是迭代器,如 [1,2].__iter__就是一个迭代器(拥有__iter__和__next__方法)
为什么要用for循环?
基于上面讲的列表这一大堆遍历方式,聪明的你立马看除了端倪,于是你不知死活大声喊道,你这不逗我玩呢么,有了下标的访问方式,我可以这样遍历一个列表啊
l=[1,2,3] index=0 while index < len(l): print(l[index]) index+=1 #要毛线for循环,要毛线可迭代,要毛线迭代器
没错,序列类型字符串,列表,元组都有下标,你用上述的方式访问,perfect!但是你可曾想过非序列类型像字典,集合,文件对象的感受,所以嘛,年轻人,for循环就是基于迭代器协议提供了一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的__iter__方法将其转换成一个迭代器,然后使用迭代器协议(调用__next__方法一个一个取值)去实现循环访问,这样所有的对象就都可以通过for循环来遍历了,而且你看到的效果也确实如此,这就是无所不能的for循环,觉悟吧,年轻人
生成器
迭代器有的好处是可以节省内存。如果在某些情况下,我们也需要节省内存,就只能自己写。我们自己写的这个能实现迭代器功能的叫生成器。
以上是关于python之路---迭代器和生成器的主要内容,如果未能解决你的问题,请参考以下文章