第八章 迭代器&生成器
一、迭代器
迭代是Python最强大的功能之一,是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象
迭代器对象从集合的第一个元素开始访问,知道所有的元素访问完结束。迭代器只能往前不会后退
迭代器有两个基本的方法:__item__()和__next__()
1. dir ()
用来查看数据类型的数据能够执行那些方法,而得到所有的带'__iter__'可以使用for循环,就是可迭代对象
print(dir(str)) E:\Anaconda\python.exe F:/老男孩python/作业训练/8.10/Train.py ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill'] Process finished with exit code 0
2. __next__()
可迭代对象可以使用__iter__()来获取到迭代器,而在迭代器中有__next__()
s = "这是一个可迭代对象" it = s.__iter__() # 获取迭代器 print(dir(it)) # 迭代器里有__iter__ 还有__next__ E:\Anaconda\python.exe F:/作业训练/8.10/Train.py ['__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__'] Process finished with exit code 0
3.特点:
- 只能向前
只能向下一步进行,不能将之前的结果重新获取
- 几乎不占用内存
只有需要的时候,他才给出运行
- for循环
# 迭代器模拟for 循环 lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] for el in lst: # 底层用的就是迭代器 print(el) it = lst.__iter__() # 获取迭代器 while 1: try: # 尝试执行 el = it.__next__() # 获取下一个元素 print(el) except StopIteration: # 处理错误 break # 当执行到最后的时候跳出
- 惰性机制
每执行一次'__next()',才给出一个结果,不执行就不给
s = "这是一个可迭代对象" it = s.__iter__() # 获取迭代器 print(it.__next__()) # "这" print(it.__next__()) # "是" print(it.__next__()) # "一" print(it.__next__()) # "个" print(it.__next__()) # "可" print(it.__next__()) # "迭" print(it.__next__()) # "代" print(it.__next__()) # "对" print(it.__next__()) # "像" print(it.__next__()) # 超出了迭代范围会报错 "print(it.__next__()) StopIteration"
4. 如何判断
from collections import Iterable # 可迭代对象 from collections import Iterator # 迭代器 lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] it = lst.__iter__() # 获取迭代器 print(isinstance(lst, Iterable)) # True 为可迭代对象 print(isinstance(lst, Iterator)) # False 不为迭代器 print(isinstance(it, Iterable)) # True print(isinstance(it, Iterable)) # True # 偏方 print("__iter__" in dir(it)) # T print("__n__" in dir(it)) # F
5. list()
list(参数)把参数进行循环迭代
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] it = lst.__iter__() s = list(it) print(s) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
此处的list如果问set 则输出的就是一个集合,其他如tuple等相同
我们可以把要迭代的内容当成子弹,然后呢获取到迭代器"__iter__()",就把子弹都装在弹夹中,然后发射就是“__next__”把每一个子弹(元素)打出来,也就是说:for 循环的时候,一开始的时候是"__"来
获取迭代器,后面每次获取元素都是通过"__next__()"来完成的,当程序遇到Stoplteration将结束循环.
二、生成器
生成器的本质就是迭代器
在Python中,使用了yield的函数被称为生成器(generatoor)
生成器一般由生成器函数或者生成器表达式来创建
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单理解生成器就是一个迭代器
在调用生成器允许的过程中,每次遇到 yield 时函数会暂时保存当前所有的运行信息,返回 yiled 的值,并在下一次执行next()方法时从当前的位置继续运行
调用一个生成器函数,默认不会执行函数体,返回的是一个迭代器对象
def func(): print("哇哈哈") yield 1 # return和yiled都能返回数据 print("呵呵呵") gen = func() # 函数中如果有yield 这个函数就是生成器函数,生成器函数生成的是生成器,这个时候不执行函数 # yield:相当于return可以返回数据,但是yield不会彻底中断函数。分段执行 gen.__next__() # 执行函数,执行到下一个yield gen.__next__() # 继续执行到下一个yield 如果没有则会报错 gen.__next__() StopIteration
1. 生成器的作用
举例需求:
当商店需要1W套学士服以备后面的使用:
def cloth(): lst = [] for i in range(0, 10000): lst.append("衣服"+str(i)) return lst cl = cloth()
这样的结果是生产商一次将1W套学士服都给商家,商家不一次性全部卖出的话就会堆积仓库,最好的方式就是商家要一套,生产商给一套,一共1W套:
def cloth(): for i in range(0, 10000): yield "衣服"+(i) cl = cloth() print(cl.__next__()) print(cl.__next__()) print(cl.__next__())
区别是:第一种是直接一次性全部拿出来,会很占内存。第二种使用生成器,一次就用一个。用多少生成多少,生成器是一个一个的指向下一个。不会回去,__next__()到哪里,指针就指到哪里.下一次继续获取指针指向的值.
由此我们可以反应出生成器的那几个特点:
- 惰性机制
- 节省内存
- 只能向前
2. send()函数
同__next__()一样都可以让生成器执行到下一个yied。
# send 函数 def num(): print("1") a = yield "2" print("a=", a) b = yield "3" print("b = ", b) c = yield "4" print("c = ", c) yield "END" gen = num() # 获取生成器 ret1 = gen.__next__() print(ret1) ret2 = gen.send("5") print(ret2) ret3 = gen.send("6") print(ret3) ret4 = gen.send("7") print(ret4) E:\Anaconda\python.exe F:/训练/8.13/Train.py 1 2 a= 5 3 b = 6 4 c = 7 END
send和__next__()区别:
- send和__next__()都是让生成器向下走一次
- send可以给上一个yield的位置传递值,不能给最后一个yield发送值,在第一次执行生成器代码的时候哦不能使用send()
3. 循环:
生成器可以使用for循环来循环获取内部的元素:
def func(): print("1") yield "2" print("3") yield "4" print("5") yield "6" print("7") yield "8" gen = func() for i in gen: print(i)