一. python的迭代协议
1.什么是迭代器:
迭代器是访问集合内数据的一种方式,一般是用来遍历数据的
2.迭代器和以下标访问的方式不一样,迭代器是不能返回的,且迭代器提供了一种惰性访问数据的方式
3.python中可迭代的对象基本都是实现了__iter__魔法函数的,迭代协议实质上就是满足__iter__魔法函数
可迭代类型
迭代器
from collections.abc import Iterable,Iteratora=[1,2]#list是可迭代的,但不是一个迭代器(list中实现了__iter__,但是没有实现__next__)print(isinstance(a,Iterable))print(isinstance(a,Iterator))'''输出:TrueFalse'''
二. 什么是迭代器和可迭代对象
1.可迭代和迭代器的区别:
from collections.abc import Iterable,Iteratora=[1,2]#迭代器,内置函数iter()会首先检查是否有__iter__,如果没有就会调用__getitem__首先创建一个迭代器iter_rator=iter(a)print(isinstance(a,Iterable))print(isinstance(a,Iterator))print(isinstance(iter_rator,Iterable))print(isinstance(iter_rator,Iterator))'''输出:TrueFalseTrueTrue'''
2.__iter__和__getitem__:
class Comany(object): def __init__(self, employee_list): self.employee_list = employee_list def __iter__(self): return 1 def __getitem__(self, item): return self.employee_list[item]if __name__ == '__main__': com = Comany(['LYQ1', 'LYQ2', 'LYQ3']) #内部实质也是iter,首先调用__iter__(如果有),否则调用__getitem for i in com: print(i)
__iter__和__getitem__都实现,首先调用__iter__
只实现__getitem__
__iter__和__getitem__都不实现
3.自己实现迭代器:
class Comany(object): def __init__(self, employee_list): self.employee_list = employee_list def __iter__(self): return MyIterator(self.employee_list)class MyIterator(Iterator): def __init__(self, emp_list): self.iter_list = emp_list self.index = 0 def __next__(self): # 真正返回迭代值的逻辑,迭代器不支持切片,但__getitem__支持且可以取任意位置的数 try: word = self.iter_list[self.index] except IndexError: raise StopIteration self.index += 1 return wordif __name__ == '__main__': com = Comany(['LYQ1', 'LYQ2', 'LYQ3']) my_itor = iter(com) # for循环内部其实也是调用next放法 while True: try: # my_itor为一个迭代器,实际在__next__中实现 print(next(my_itor)) except StopIteration: pass
三. 生成器函数使用
1.什么是生成器函数:函数里只要有yeild关键字:
生成器函数返回的是一个生成器对象,是在python编译字节码的时候就产生了;
return只能有一个返回值,而yield可以返回多个,生成器对象实现了迭代协议;
惰性求值,延迟求值提供了可能。
def gen_fun(): #首先把值返回给调用方 #然后又调用__next__调用下一个(实现了生成器协议) yield 'a' yield 'b'def fun(): return 1#生成器函数返回的是一个生成器对象,实在python编译字节码的时候就产生了#return只能有一个返回值,而yield可以返回多个c1 = gen_fun()c2 = fun()print(c1)for i in c1: print(i)print(c2)'''ab1'''
2.例:
使用return:
#斐波拉契过程def fib(index): re_list=[] n,a,b=0,0,1 while n
使用生成器:
#使用生成器的斐波拉契过程def gen_fib(index): n,a,b=0,0,1 while n
四. 生成器的原理
1.函数工作原理:
python.exe会用一个叫做PyEval_EvalFrameEx(c语言函数)去执行函数,首先会创建一个栈帧(stack frame)
def foo(): bar()def bar(): pass#python.exe会用一个叫做PyEval_EvalFrameEx(c语言函数)去执行foo()函数,首先会创建一个栈帧(stack frame)"""python一切皆对象,栈帧对象, 字节码对象当foo调用子函数 bar, 又会创建一个栈帧所有的栈帧都是分配在堆内存上,这就决定了栈帧可以独立于调用者存在"""
def foo(): bar()def bar(): pass#查看字节码,以下为查看foo函数的字节码import disprint(dis.dis(foo))''' 2 0 LOAD_GLOBAL 0 (bar) ——》加载目标函数 2 CALL_FUNCTION 0 ——》执行函数 4 POP_TOP ——》从栈顶端pop出 6 LOAD_CONST 0 (None)——》加载return值,没有就None 8 RETURN_VALUE ——》将值return'''
import inspectframe=Nonedef foo(): bar()def bar(): global frame frame=inspect.currentframe()foo()#即使函数运行完成,也可以查看函数的栈帧,是放在堆上面的,和静态语言不一样(放在栈上,运行后就销毁了)print(frame.f_code.co_name)caller_frame=frame.f_backprint(caller_frame.f_code.co_name)'''输出:barfoo'''
2.生成器工作原理:(生成器对象也是放在堆内存中的,可以独立于调用者存在,任何地方都可以控制它)
函数工作原理
对生成器对象封装,f_lasti: 最近执行代码字节码的位置,以及f_locals:变量,因为有这两个变量,就可以不断循环函数
def gen_fun(): #生成器函数可以return一个值,早期的python不行 #编译字节码时,识别了关键词yield,标记函数 yield 1 name='LYQ' yield 2 age=20 return 'HaHa'import disgen=gen_fun()print(dis.dis(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)next(gen)print(gen.gi_frame.f_lasti)print(gen.gi_frame.f_locals)'''输出: 4 0 LOAD_CONST 1 (1) 2 YIELD_VALUE 4 POP_TOP 5 6 LOAD_CONST 2 ('LYQ') 8 STORE_FAST 0 (name) 6 10 LOAD_CONST 3 (2) 12 YIELD_VALUE 14 POP_TOP 7 16 LOAD_CONST 4 (20) 18 STORE_FAST 1 (age) 8 20 LOAD_CONST 5 ('HaHa') 22 RETURN_VALUENone-1——》还没开始执行{}2 ——》执行到第一个yield的位置(yield 1){}12——》执行到第二个yield的位置(yield 2){'name': 'LYQ'}——》把局部变量拿出来了'''
五. 通过UserList来看生成器的应用
实现了遍历list,i是局部变量,从0开始,一步一步返回值
六. 生成器实现大文件读取
#读取大文件def myreadlines(f, newline): #buf相当于缓存 buf = "" while True: while newline in buf: pos = buf.index(newline) yield buf[:pos] #更新截断buf(以分割符) buf = buf[pos + len(newline):] #每次读取4096个字符(接着上一次的) chunk = f.read(4096) if not chunk: #说明已经读到了文件结尾 yield buf break buf += chunkwith open("input.txt") as f: for line in myreadlines(f, "{|}"): print (line)