zoukankan      html  css  js  c++  java
  • 迭代器和生成器

    一. python的迭代协议

      1.什么是迭代器:

        迭代器是访问集合内数据的一种方式,一般是用来遍历数据的    

      2.迭代器和以下标访问的方式不一样,迭代器是不能返回的,且迭代器提供了一种惰性访问数据的方式

      3.python中可迭代的对象基本都是实现了__iter__魔法函数的,迭代协议实质上就是满足__iter__魔法函数

    可迭代类型

    迭代器

    from collections.abc import Iterable,Iterator
    a=[1,2]
    #list是可迭代的,但不是一个迭代器(list中实现了__iter__,但是没有实现__next__)
    print(isinstance(a,Iterable))
    print(isinstance(a,Iterator))
    '''
    输出:
    True
    False
    '''

    二. 什么是迭代器和可迭代对象

      1.可迭代和迭代器的区别:

    from collections.abc import Iterable,Iterator
    a=[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))
    '''
    输出:
    True
    False
    True
    True
    '''

      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 word
    
    
    if __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)
    '''
    <generator object gen_fun at 0x000001AF4399D048>
    a
    b
    1
    '''

      2.例:

        使用return:

    #斐波拉契过程
    
    def fib(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
    #若果index太大,十分消耗内存,这时就可以用到yield
    print(fib(10))
    '''
    输出:
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    '''

        使用生成器:

    #使用生成器的斐波拉契过程
    
    def gen_fib(index):
        n,a,b=0,0,1
        while n<index:
    #将值yield,不会消耗内存    
            yield b
            a,b=b,a+b
            n+=1
    for i in gen_fib(10):
        print(i)

    四. 生成器的原理

      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 dis
    print(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 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
    '''

       2.生成器工作原理:(生成器对象也是放在堆内存中的,可以独立于调用者存在,任何地方都可以控制它)

    函数工作原理

    对生成器对象封装,f_lasti: 最近执行代码字节码的位置,以及f_locals:变量,因为有这两个变量,就可以不断循环函数

    def gen_fun():
        #生成器函数可以return一个值,早期的python不行
        #编译字节码时,识别了关键词yield,标记函数
        yield 1
        name='LYQ'
        yield 2
        age=20
        return 'HaHa'
    import dis
    gen=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_VALUE
    None
    -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 += chunk
    
    with open("input.txt") as f:
        for line in myreadlines(f, "{|}"):
            print (line)
  • 相关阅读:
    向Url发送post请求传递参数
    表格列转行
    C#匹配HTML中的图片地址
    C# 正则表达式(备忘录)
    将天文数字转换成中文大写
    搜索引擎优化排名因素小结
    WPF概述
    将中文汉字转换成拼音(全拼)
    提取网页中的链接并生成xml
    快速排序算法的实现
  • 原文地址:https://www.cnblogs.com/lyq-biu/p/10438189.html
Copyright © 2011-2022 走看看