08-迭代器和生成器
一、迭代器和生成器
迭代是Python最强大的功能之一,是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。迭代器有两个基本的方法:iter() 和 next()。
在 Python 中,使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。调用一个生成器函数,返回的是一个迭代器对象。
1.1、python中的迭代协议
什么是迭代协议? 迭代协议:一个是 __iter__(也就是可迭代类型),另一个是Iterator(迭代器),迭代器是什么? 迭代器是访问集合内元素的一种方式, 一般用来遍历数据。迭代器和以下标的访问(list[0])方式不一样, 迭代器是不能返回的, 迭代器提供了一种惰性数据的方式。
#list[item]访问的原理是__getitem__ #[] list , __iter__ #Iterable继承的是抽象基类(metaclass=ABCMeta)(__iter__) #Iterator(迭代器)这个是继承了Iterable,实现的魔法函数有__iter__,__next__ from collections.abc import Iterable, Iterator a = [1,2] #list是实现__iter__是可迭代类型,可迭代类型与迭代器(__iter__,__next__)是不一样的 print(isinstance(a, Iterable))#True print(isinstance(a, Iterator))#False
1.2、什么是迭代器和可迭代对象
from collections.abc import Iterator class Company(object): def __init__(self, employee_list): self.employee = employee_list def __iter__(self): return MyIterator(self.employee) #返回的是迭代器,这样自定义迭代器复用性更强 # def __getitem__(self, item): #能切片,迭代器是不能切片的 # return self.employee[item] class MyIterator(Iterator): def __init__(self, employee_list): self.iter_list = employee_list self.index = 0 #用迭代器维护这个变量 def __next__(self): #真正返回迭代值的逻辑 try: res= self.iter_list[self.index] except IndexError: raise StopIteration self.index += 1 return res if __name__ == "__main__": company = Company(["li", "lily", "john"]) my_itor = iter(company) # while True: #for循环的实现原理 # try: # print (next(my_itor)) # except StopIteration: # pass # 当调用迭代器的时候会这样调用next(my_itor) for item in company: #当调用for循环的时候,它会尝试去调用iter(company),首先找的是__iter__, print(item) # 如果没有的话,它就会找__getitem__,
1.3、生成器函数的使用
生成器函数,函数里只要有yield关键字。
def gen_func():#提交的值都能for循环打印出来 yield 1 yield 2 yield 3 def fib(index): #1\求斐波拉契只能看见最终结果,看不到过程 if index <= 2: return 1 else: return fib(index-1) + fib(index-2) def fib2(index): #2\改动之后,将每次产生的值添加到列表中,就可以看到过程中产生的值 re_list = [] n,a,b = 0,0,1 while n<index: re_list.append(b) a,b = b, a+b n += 1 return re_list def gen_fib(index):#3\用生成器改动,将每次结果yield出来, n,a,b = 0,0,1 while n<index: yield b a,b = b, a+b n += 1 # 斐波拉契 0 1 1 2 3 5 8 #惰性求值, 延迟求值提供了可能 def func(): return 1 if __name__ == "__main__": #生成器对象, python编译字节码的时候就产生了, gen = gen_func() for value in gen: print (value,end=' ') #1 2 3 # re = func() #将yield提交的值遍历出来 for data in gen_fib(10): print(data,end=' ') #1 1 2 3 5 8 13 21 34 55
1.4、python是如何实现生成器的
#1.python中函数的工作原理 import inspect frame = None def foo(): bar() def bar(): global frame frame = inspect.currentframe() #python.exe会用一个叫做 PyEval_EvalFramEx(c函数)去执行foo函数, 首先会创建一个栈帧(stack frame) """ python一切皆对象,栈帧对象,编译成字节码对象 当foo调用子函数 bar, 又会创建一个栈帧 所有的栈帧都是分配在堆内存(栈帧一直在堆内存之中)上,这就决定了栈帧可以独立于调用者存在 """ # import dis # print(dis.dis(foo)) #查看运行的字节码过程 foo() print(frame.f_code.co_name) #bar caller_frame = frame.f_back print(caller_frame.f_code.co_name) #foo #虽然这个函数已经运行完成,我们照样可以去堆内存中找到它的栈帧 def gen_func(): yield 1 name = "lishuntao" yield 2 age = 18 return "HUT" import dis gen = gen_func() #返回生成器对象 print (dis.dis(gen)) #人为控制它的执行流程 print(gen.gi_frame.f_lasti) #-1 f_lasti:可以知道进行到哪一个步骤了 print(gen.gi_frame.f_locals) #{} next(gen) #进行下一步流程 print(gen.gi_frame.f_lasti)#2 print(gen.gi_frame.f_locals)#{} next(gen) print(gen.gi_frame.f_lasti)#12 print(gen.gi_frame.f_locals)#{"name":"lishuntao"}
1.5、生成器在UserList中的应用
class Company: def __getitem__(self, item): pass def __iter__(self): pass company = Company() iter(company) #调用iter的时候,会去对象中寻找__iter__,如果没有的话就去寻找__getitem__ #生成器在list中的应用,如果我们要继承list的话,就继承UserList,因为list是C语言写出来的,UserList是利用python写出来的。 from collections import UserList #UserList中继承的序列Sequence中的__iter__里面就运用生成器实现的数据迭代
1.6、生成器如何读取大文件
当在工作中,应用生成器提取大文件(一行大文件)。
#大文件, 特殊的一行 def myreadlines(f, newline): buf = "" #设置空的缓存区 while True: #第一次进入buf为空,直接跳到下面读取文件 while newline in buf: pos = buf.index(newline) yield buf[:pos] #提交第一行 buf = buf[pos + len(newline):] #将提交出去的舍弃掉 chunk = f.read(4096) if not chunk: #说明已经读到了文件结尾,然后将buf提交出去 yield buf break buf += chunk with open("input.txt") as f: for line in myreadlines(f, "{|}"): print (line) """ Prior to beginning tutoring sessions , I ask new students to fill out a brief self-assessment where they rate their understanding of various Python concepts. Some topics ("control flow with if/else" or "defining and using functions") are understood by a majority of students before ever beginning tutoring. There are a handful of topics, however, that almost all students report having no knowledge or very limited understanding of. Of these """