迭代器
什么叫做迭代器?
在回答这个问题之前应该先要了解到什么叫做迭代
>>> dir([1, 2]) ['__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'] >>> dir((1, 3, 4)) ['__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']
我们查看可迭代的数据类型列表以及元组的属性时,都能找到一个共同的双下方法"__iter__"
可迭代协议就是:只要含有__iter__()方法的都是可迭代的。
那什么又叫做迭代器那?迭代器跟可迭代又有什么关系?
再看一段代码
>>> print([1, 2].__iter__()) <list_iterator object at 0x0000002DE6FDFDD8> # iterator 迭代器的意思
到这里其实就已经找到了答案。什么叫做迭代器啦?就是可迭代数据类型调用[ ].__iter__()方法,返回的就是一个迭代器
迭代器有什么用?
>>> dir([1, 2].__iter__()) ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
仔细观察上面的双下方法会发现三个从前没有接触过的
__length_hint__() # 获取迭代器中元素的长度 __setstate__() # 根据索引值指定从哪里开始迭代 __next__() # 一个一个的取值
>>> l = [1, 2, 3, 4, 5, 6, 7] >>> l_it = l.__iter__() >>> l_it.__length_hint__() 7 >>> l_it.__setstate__(2) >>> l_it.__next__() 3 >>> l_it.__next__() 4 >>>
其实啊迭代器的作用关键就在__next__()方法。在取数据的时候一条一条的取。有时候在读取一些不知道多大内存的文件时,显然__next__()会比写for循环取更安全。
生成器
生成器的本质:就是一个迭代器,它是由我们自定义的,且是惰性运算的
python中的生成器
- 生成器表达式
- 生成器函数
生成器表达式
>>> it = (i for i in range(5)) >>> it <generator object <genexpr> at 0x0000002DE6FDB830> >>> it.next() # 貌似py3不支持这种写法 Traceback (most recent call last): File "<pyshell#17>", line 1, in <module> it.next() AttributeError: 'generator' object has no attribute 'next' >>> it.__next__() 0 >>> it.__next__() 1
生成器表达式是不是可以叫做元组解析、元组表达式?
生成器函数
>>> def produce(): """生产娃娃""" for i in range(2000000): yield "生产了%d个娃娃" % i >>> product_g = produce() # 调用生成器函数 >>> product_g <generator object produce at 0x000000E884B51C50> >>> product_g.__next__() '生产了0个娃娃' >>> product_g.__next__() '生产了1个娃娃'
生成器函数的定义也特别简单就是返回值由return变成了yield
生成器的方法send
def generator(): print(123) yield 1 print(456) arg = yield 2 yield arg g = generator() ret = g.__next__() print(ret) ret = g.send('hello') # 没有变量来接收 print(ret) ret = g.send('hello') # send的参数被arg接收到了 print(ret) # 返回arg >>>123 1 456 2 hello
# 利用生成器求平均值 def average(): sum = 0 count = 0 avg = 0 while True: num = yield avg sum += num count += 1 avg = sum / count avg_g = average() avg_g.__next__() avg1 = avg_g.send(10) print(avg1) avg2 = avg_g.send(20) print(avg2)
注意send的用法:
send的作用和__next__()一样,send能够传递参数给生成器函数,前提是要有变量来接收
生成器第一次取值不能使用 send
生成器函数的最后一个yield不能接收新的值