美文网首页
【python】迭代器&生成器

【python】迭代器&生成器

作者: 我写的BUG代码少 | 来源:发表于2020-04-23 10:17 被阅读0次

    B站视频链接

    目录

    一. 迭代器

    1.可迭代协议&迭代器协议(Iterable/Iterator)

    • 只要是能被for循环的数据类型就一定拥有iter方法
    • 【==可迭代协议(Iterable)==】只要含有iter方法的都是可迭代的(就可以被for循环)
      • 判断是否含有iter方法(是否可以被for循环)
        print('__iter__' in dir(dict))
        >>> True
        
    • for 循环先去找iter方法,找到后开始遍历,如果没找到就报错
      • for循环实际是在使用迭代器
      • 可以被for循环的东西
        • list
        • dict
        • str
        • set
        • tuple
        • f = open() #文件
        • range()
        • enumerate
    • (可迭代的东西).__iter__() >>> 迭代器
      • 一个列表执行了iter()之后的返回值就是一个迭代器
        [].__iter__() #迭代器
        
    • 迭代器包含·的方法有:
      #【1】__setstate__
      
      #【2】__next__    #通过next可以从迭代器中一个一个的取值
      
      #【3】__length_hint__
      [0,1].__iter__().__length_hint__() #元素个数
      >>> 2
      
    • 【==迭代器协议(Iterator)==】内部同时含有nextiter方法的就是迭代器
      from collections import Iterator
      
      class A:
          def __iter__(self): pass
          def __next__(self): pass
      
      a = A()
      print(isinstance(a, Iterator)) 
      >>> True
      
      • 模拟for循环
      list1 = [1,2,3,4]
      iterator = list1.__iter__()
      while True:
          print(iterator.__next__())
      

    2.迭代器的好处

    • 从容器类型中一个一个的取值,会把所有值都取到
    • 节省内存空间
      • 迭代器并不会在内存中再占用一大块内存
        • 而是随着循环 每次生成一个
        • 每次next每次给我一个
      range(10000000000)  #类似定义,但还未读取占空间
      print(list(range(10000000000)))
      

    二. 生成器(自己写的迭代器)

    1.生成器函数(自己写的函数)

    • 含有yield关键字的函数,是生成器函数
    • 特点:
      • 调用函数之后,函数不执行,返回一个生成器
      • 每次调用next方法的时候会取到一个值
      • 直到取完最后一个,再执行next会报错
    • yield不能与return共用,且需要写在函数内部
      • return会会结束
      • yelid后不会结束
    def generator():  #1.定义生成器函数
        print(1)
        yield 'a'
    
    ret = generator() #2.生成器函数调用generator(),并得到返回值(返回值ret是一个生成器) #3.返回的生成器 >>> ret
    print(ret)   #>>> <generator object generator at 0x000001BC7CC2A5C8> 说明内部代码未被执行,ret是生成器 
    print(ret.__next__()) #ret是生成器(迭代器)含有next方法,可调用__next__()
    
    >>>
    1
    a
    

    [图片上传失败...(image-eb4702-1587608216353)]

    • 生成器可阶段性取值(每做一部分就可以取值)
    def func():
        for i in range(2000000):
            yield '取值%s'%i
    
    g = func()  #生成器1
    g1 = func()  #生成器2(新生成器,与生成器1无关)
    
    #从生成器1取50个
    count = 0
    for i in g:
        count+=1
        print(i)
        if count >50:
            break
    
    #从生成器2继续取50个(共取100个)
    for i in g:
        count+=1
        print(i)
        if count >100:
            break
    

    监听文件输入的例子

    def tail(filename):
        f = open(filename, encoding='utf-8')
        while True:
            line = f.readline()
            if line.strip(): #如果line.strip()[去掉回车]不为空
                yield line.strip() #不能用return,用return只能取到第一行
                
    g = tail('file')
    for i in g:
        if 'python' in i:  #监听过滤/有‘python’就打印,没有就不打印
            print(i)
    

    send()的用法

    • send() 获取下一个值的效果和next基本一致
    • 只是再获取下一个值的时候,给上一个yield的位置传一个数据
    • 使用send的注意事项
      • 第一次不能用send
      • 第一次使用生成器的时候,是用next获取下一个
      • 函数中最后一个yield不能接收新的值
    def generator():
        print(123)
        content = yield 1
        print('=====',content)
        print(456)
        yield 2
    
    g = generator()
    ret = g.__next__()
    print('***',ret)
    ret = g.send('hello') #send的效果和next一样
    print('***',ret)
    
    >>>
    123
    *** 1
    ===== hello
    456
    *** 2
    

    [图片上传失败...(image-cb1cf0-1587608216353)]

    • 函数执行:先执行等号右边,再执行左边
    • 执行第一个next的时候,先执行到等号右边的yield,此时还没有赋值给content;下一步send的值发送给yield并赋值给等号左边的content

    如果yield后还有要执行的代码

    def generator():
        print(123)
        content = yield 1
        print('=====',content)
        print(456)
        arg = yield 2
        ''' 代码语句'''
        yield    #这里yield空
    
    
    g = generator()
    ret = g.__next__()
    print('***',ret)
    ret = g.send('hello') 
    print('***',ret)
    

    获取移动平均值

    def average():
        sum = 0
        count = 0
        avg = 0
        while True:
            num = yield avg
            sum += num
            count += 1
            avg = sum/count
            
    
    avg_g = average()
    avg_g.__next__()
    avg1 = avg_g.send(10)
    avg1 = avg_g.send(20)
    '''......'''
    print(avg1)
    
    预激生成器的装饰器
    def init(func):
        def inner(*args,**kwargs):
            g = func(*args,**kwargs)
            g.__next__()
            return g
        return inner
    
    @init
    def average():
        sum = 0
        count = 0
        avg = 0
        while True:
            num = yield avg
            sum += num
            count += 1
            avg = sum/count
            
    
    avg_g = average()
    avg1 = avg_g.send(10)
    avg1 = avg_g.send(20)
    print(avg1)
    

    [图片上传失败...(image-7007f9-1587608216353)]

    yield from

    • 原代码
    def generator():
        a = 'abchdhb'
        b = '123555'
        for i in a:
            yield i
        for i in b:
            yield i
    
    g = generator()
    for i in g:
        print(i)
    
    • 修改后
    def generator():
        a = 'abchdhb'
        b = '123555'
        yield from a
        yield from b
    
    g = generator()
    for i in g:
        print(i)
    

    2.生成器表达式

    #【生成器表达式】
    g = (i for i in range(10)) #g是生成器
    for i in g:
        print(i)
    
    #【列表表达式】
    list = [i for i in range(10)]
    for i in list:
        print(i)
    
    • 区别:
      • 括号不一样
      • 返回的值不一样(生成器表达式几乎不占用内存)

    面试题

    • 1.生成器中的数据只能取一次,取完就没有了
    • 2.惰性运算:不找它取值,它就不工作

    面试题1

    def demo():
        for i in range(4):
            yield i
    
    g = demo()
    
    g1 = (i for i in g)
    g2 = (i for i in g1)
    
    print(list(g1))
    print(list(g2)) #g1的值已经被取完了
    >>>
    [0, 1, 2, 3]
    []
    

    面试题2: for循环+生成器

    def add(n,i):
        return n+i
    
    def test():
        for i in range(4):
            yield i
    
    g = test()
    
    for n in [1,10]:
        g = (add(n,i) for i in g)
    '''
    n = 1
    g = (add(n,i) for i in g) #1
    n = 10
    g = (add(n,i) for i in g)  #都未执行,直到print语句才执行,这个时候再找g
    >>>
    n = 1
    g = (add(n,i) for i in g)
    n = 10
    g = (add(n,i) for i in (add(n,i) for i in g)) #这步的g是1.的g
    >>>
    n = 1
    g = (add(n,i) for i in g)
    n = 10
    g = (add(n,i) for i in (add(n,i) for i in test()))
    >>>
    n = 1
    g = (add(n,i) for i in g)
    n = 10
    g = (add(n,i) for i in (add(10,i) for i in test())) #i=0,1,2,3
    #g = (add(n,i) for i in (10,11,12,13))
    #g = (add(10,i) for i in (10,11,12,13))
    '''
    
    print(list(g))
    >>>
    [20, 21, 22, 23]
    
    def add(n,i):
        return n+i
    
    def test():
        for i in range(4):
            yield i
    
    g = test()
    for n in [1,10,5]:
        g = (add(n,i) for i in g)
    '''
    n = 1
    g = (add(n,i) for i in test())
    n = 10
    g = (add(n,i) for i in add(n,i) for i in test())
    n = 5
    g = (add(n,i) for i in add(n,i) for i in add(n,i) for i in test())
    #g = (add(5,i) for i in add(5,i) for i in add(5,i) for i in test())
    '''
    print(list(g))
    

    识别多个文件中的特殊字段,并返回文件名

    • 一个文件夹下,有多个文件,找寻含有'python'字段的文件,并返回文件名
    import os
    
    def init(func):
        def wrapper(*args,**kwargs):
            g=func(*args,**kwargs)
            next(g)
            return g
        return wrapper
    
    @init
    def list_files(target):
        while 1:
            dir_to_search=yield
            for top_dir,dir,files in os.walk(dir_to_search):
                for file in files:
                    target.send(os.path.join(top_dir,file))
    @init
    def opener(target):
        while 1:
            file=yield
            fn=open(file)
            target.send((file,fn))
    @init
    def cat(target):
        while 1:
            file,fn=yield
            for line in fn:
                target.send((file,line))
    
    @init
    def grep(pattern,target):
        while 1:
            file,line=yield
            if pattern in line:
                target.send(file)
    @init
    def printer():
        while 1:
            file=yield
            if file:
                print(file)
    
    g=list_files(opener(cat(grep('python',printer()))))
    
    g.send('/test1')
    
    协程应用:grep -rl /dir
    

    相关文章

      网友评论

          本文标题:【python】迭代器&生成器

          本文链接:https://www.haomeiwen.com/subject/ugkoihtx.html