博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
迭代器和生成器
阅读量:6271 次
发布时间:2019-06-22

本文共 5060 字,大约阅读时间需要 16 分钟。

一. 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)

 

转载于:https://www.cnblogs.com/lyq-biu/p/10438189.html

你可能感兴趣的文章
RabbitMQ三种Exchange模式(fanout,direct,topic)的性能比较
查看>>
Spring JavaBean属性值的注入方式( 属性注入, 特殊字符注入 <![CDATA[ 带有特殊字符的值 ]]> , 构造器注入 )...
查看>>
【Linux】Linux下统计当前文件夹下的文件个数、目录个数
查看>>
Hibernate_14_数据连接池的使用
查看>>
Codeforces Round #271 (Div. 2) D. Flowers (递推 预处理)
查看>>
jacky自问自答-java并发编程
查看>>
Struts2+JSON数据
查看>>
zTree实现单独选中根节点中第一个节点
查看>>
Cocos2D-x设计模式发掘之中的一个:单例模式
查看>>
很强大的HTML+CSS+JS面试题(附带答案)
查看>>
用树莓派实现RGB LED的颜色控制——C语言版本号
查看>>
VC2012编译CEF3-转
查看>>
java 自己定义异常,记录日志简单说明!留着以后真接复制
查看>>
Android 使用AIDL实现进程间的通信
查看>>
机器学习(Machine Learning)&深度学习(Deep Learning)资料
查看>>
jquery的图片轮播 模板类型
查看>>
C# 获取文件名及扩展名
查看>>
Web安全学习计划
查看>>
输出有序数组的连续序列范围
查看>>
zinnia项目功能分析
查看>>