gj9 迭代器和生成器
9.1 python的迭代协议
list内部实现了__iter__()协议(魔法函数),是可迭代对象,但还不是迭代器(迭代器需要实现__next__协议)
生成器实现了__iter__(),__next__()协议,因此是迭代器。但迭代器不一定是生成器(不具有生成器的方法协议)
#什么是迭代协议 #迭代器是什么? 迭代器是访问集合内元素的一种方式, 一般用来遍历数据 #迭代器和以下标的访问方式不一样, 迭代器是不能返回的, 迭代器提供了一种惰性方式数据的方式 #[] list , __iter__ from collections.abc import Iterable, Iterator a = [1,2] iter_rator = iter(a) print (isinstance(a, Iterable)) print (isinstance(a, Iterator)) print (isinstance(iter_rator, Iterator)) --- True False True
9.2 什么是迭代器和可迭代对象
from collections.abc import Iterator class Company(object): def __init__(self, employee_list): self.employee = employee_list def __iter__(self): # 可迭代对象,实现了__iter__ return MyIterator(self.employee) # iter(可迭代对象) 方法 会调用反回迭代器 # def __getitem__(self, item): # 当没有定义__iter__协议for循环就会将定义的getitem变成迭代器 # return self.employee[item] # 退化为迭代器 # 可迭代对象不要去实现__next__ class MyIterator(Iterator): # 迭代器 实现了__next__ def __init__(self, employee_list): self.iter_list = employee_list self.index = 0 def __next__(self): #真正返回迭代值的逻辑 #实现原理 try: word = self.iter_list[self.index] except IndexError: raise StopIteration self.index += 1 return word if __name__ == "__main__": company = Company(["tom", "bob", "jane"]) my_itor = iter(company) for item in company: print (item) # --- tom bob jane
9.3 生成器函数使用
#生成器函数,函数里只要有yield关键字 def gen_func(): yield 1 yield 2 yield 3 def func(): return 1 if __name__ == "__main__": #生成器对象, python编译字节码的时候就产生了, gen = gen_func() # <generator object gen_func at 0x000001CD083334F8> print(gen) for value in gen: print (value) # --- <generator object gen_func at 0x000001CD08333570> 1 2 3 # ---
# 斐波拉契 0 1 1 2 3 5 8 # 惰性求值, 延迟求值提供了可能 def fib(index): # 第几个数 if index <= 2: return 1 else: return fib(index-1) + fib(index-2) def fib2(index): # 打印前几个数 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 print(fib(10)) print(fib2(10)) # --- 55 [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] # --- def gen_fib(index): n,a,b = 0,0,1 while n<index: yield b a,b = b, a+b n += 1 for data in gen_fib(10): print (data) # --- 1 1 2 3 5 8 13 21 34 55
9.4 生成器的原理
#1.python中函数的工作原理 def foo(): bar() def bar(): pass #python.exe会用一个叫做 PyEval_EvalFramEx(c函数)去执行foo函数, 首先会创建一个栈帧(stack frame) """ python一切皆对象,栈帧对象, 字节码对象 当foo调用子函数 bar, 又会创建一个栈帧 所有的栈帧都是分配在堆内存上,这就决定了栈帧可以独立于调用者存在 """ import dis print(dis.dis(foo)) #--- 5 0 LOAD_GLOBAL 0 (bar) 2 CALL_FUNCTION 0 4 POP_TOP 6 LOAD_CONST 0 (None) 8 RETURN_VALUE None #--- import inspect frame = None def foo(): bar() def bar(): global frame frame = inspect.currentframe() foo() print(frame.f_code.co_name) #栈针 caller_frame = frame.f_back # 调用他的栈针 print(caller_frame.f_code.co_name) # --- bar foo
def gen_func(): yield 1 name = "lewen" yield 2 age = 30 return "imooc" import dis gen = gen_func() print (dis.dis(gen)) # --- 2 0 LOAD_CONST 1 (1) 2 YIELD_VALUE 4 POP_TOP 3 6 LOAD_CONST 2 ('lewen') 8 STORE_FAST 0 (name) 4 10 LOAD_CONST 3 (2) 12 YIELD_VALUE 14 POP_TOP 5 16 LOAD_CONST 4 (30) 18 STORE_FAST 1 (age) 6 20 LOAD_CONST 5 ('imooc') 22 RETURN_VALUE None # --- print(gen.gi_frame.f_lasti) print(gen.gi_frame.f_locals) next(gen) print(gen.gi_frame.f_lasti) print(gen.gi_frame.f_locals) next(gen) print(gen.gi_frame.f_lasti) print(gen.gi_frame.f_locals) # --- -1 {} 2 {} 12 {'name': 'lewen'}
9.5 通过UserList来看生成器的应用
from collections import UserList # 用Python实现的list
9.6 生成器实现大文件读取
# 500G, 特殊 全部在 一行,由特殊分割符分割
# 一次性读取太大,按分隔符读取 def myreadlines(f, newline): buf = "" # 缓存 while True: while newline in buf: pos = buf.index(newline) yield buf[:pos] # 将第一个分隔符之前的数据yield 出去 buf = buf[pos + len(newline):] # 丢弃掉yield的数据,如果buf中还有分隔符就继续处理, chunk = f.read(4096) # 没有就继续读新的4096个字节 if not chunk: # 说明已经读到了文件结尾 yield buf break buf += chunk with open("input.txt") as f: for line in myreadlines(f, "{|}"): print(line)