• Python之迭代器、生成器


    1、列表生成式

    # 普通生成列表
    ab=[]
    for x in range(10):
        ab.append(x*x)
    print(ab)
    
    # 列表表达式
    a=[x*x for x in range(10)] # 这就叫列表生成式,也叫声明式编程
    '''
    1:先取元素
    2:把取出来的元素进行一个处理 x*x
    3:把处理完的元素依次存放到列表里
    print(a)
    '''
    
    egg=[x for x in range(100) if x<50]    # 如果 x 小于  50,就加进去 egg 的列表
    print(egg)
    
    缺点:
    如果可迭代的对象很大,会把可迭代的内容全放到内存处理,占满内存,解决这个问题用生成器表达式

    2、生成器表达式

    #生成器表达式
    egg = (x for x in range(100) if x < 50) # 这就叫生成器表达式,与列表生成式的区别就是用小括号
    
    print(egg)  # <generator object <genexpr> at 0x000001FCDB352E60>  生成器对象
    
    # 如果想要拿里面的值通过 for 循环,或者 next 方法
    for i in egg:
        print(i) 
    
    print(next(egg))
    print(next(egg))
    print(next(egg))
    print(next(egg))
    
    '''
    跟列表生成式的区别
    列表生成式有索引,取值灵活,能获取长度
    生成器表达式省内存,用一个取一个,不能获取长度,没有索引
    '''

    3、迭代器

    '''
    迭代器协议满足两个条件
    1:对象可以被__iter__方法转换成迭代器的,或 iter(obj)
    2:对象可以通过__next__方法取值的,或 next(obj)
    '''
    
    l=[1,2,3,5]  # 列表是一个可迭代对象,不是迭代器,有 iter 方法
    
    d=iter(l)  # 或者  l.__iter__(),把列表用 iter 函数或__iter__()方法,转换为一个迭代器
    print(d)  # <list_iterator object at 0x0000000000B16B38>
    
    
    # 优点:
    '''
    1:迭代器提供了一种不依赖索引的取值方式,这样就可以遍历那种没有索引的可迭代对象(字典,集合,文件)
    
    2:迭代器与列表比较,迭代器是惰性计算的,更节省内存,什么叫惰性计算,列表可迭代对象,和迭代器比较,可以知道可迭代对象的长度len方法,但无法知道迭代器的长度,迭代器每next 只拿一次数据
    '''
    
    # 缺点:
    '''
    一次性的,只能往后取值,不能倒着取值
    '''

    取迭代器里的内容

    print(d.__length_hint__())  # 获取迭代器元素的长度
    d.__setstate__(2)   # 设置迭代器索引值的开始
    print(next(d))  # 用 next 方法取迭代器对象里的值 
    print(next(d))
    print(next(d))  # 报错,为什么,看下面 while 和 for
    
    
    while True:
        try:
            print(next(d))      # 循环取迭代器里的内容
        except StopIteration:   # 去捕捉 StopIteration 的错误
            break   # 一旦捕捉到,就退出
    
    
    for i in d:
        print(i)
    '''
    for 循环内部一共做了三件事
    1:调用可迭代对象(d)的 __iter__ 方法返回一个迭代器对象
    2:不断调用迭代器对象的 next 方法,去取值
    3:处理 stopiteration 异常,原理跟上面的 while 差不多
    '''
    
    
    from collections import Iterator,Iterable
    print(isinstance(l,Iterable))  判断是否为可迭代对象
    iterator 迭代器
    iterable 可迭代
    # 格式 isinstance(对象,类型)
    isinstance方法:判断对象是否为指定的类型,是返回 True,否返回 False

    4、生成器

    注意:生成器都是迭代器,迭代器不一定是生成器

    # 创建生成器:

    a = (x*2 for x in range(5))    # 通过生成器表达式创建生成器
    
    
    # 函数里面有 yield 关键字表示这个函数是一个生成器,其内部是把函数做成迭代器
    def foo():
        print('ok')
        yield 1   # 返回值,类似 return,返回给 next()
        print('ok2')
        yield 2   # 返回值,类似 return
    
    g=foo()  # foo() 返回一个生成器对象
    print(g)  #<generator object foo at 0x000000000083A518>
    next(g) # 显示的是 ok:返回值是 1,生成器也是迭代器,通过 next 方法取值
    next(g) # 显示的是 ok2:返回值 2
    # a=next(g)   # 1  ,屏幕会输出 ok
    # b=next(g)   # 2  ,屏幕会输出 ok2
    # print(a,b)
    
    
    for 循环过程
    for i in foo(): 
        while True:
            i=next(foo()) #先执行 next(foo())返回 print(ok),然后把 yield 的值返回给 i,print(i)=1
    ok
    1
    ok2
    2

    # 生成器就是一个可迭代对象(iterable)

    生成器自动实现了迭代器协议

    for i in a: 
        print(i)
    # for 会对 a 对象进行一个 next 的方法调用,循环取一个值,取第二个值,第一个值没有变量引用,会被内存垃圾回收了,达到节省内存的目的

    # 生成器 yield 与 函数 return有何区别?

    return只能返回一次函数就彻底结束了,而yield能返回多次值

    # yield到底干了什么事情:

    yield 把 __iter__ 和 __next__ 方法封装到函数内部使其变成生成器-->迭代器

    用return返回值能返回一次,而yield返回多次

    函数在暂停以及继续下一次运行时的状态是由yield保存

    send 方法

    def bar():
        print('ok')
        count=yield 1
        print(count)
        print('ok2')
        yield 2
    b=bar()  #生成一个生成器对象
    ret=b.send(None)  #b.send(None)=next(b)  第一次 send 前如果没有 next,只能传一个 None,并返回 yield 的值;这里赋给 ret,或者先 next(b) 执行一下,也可以写一个初始化生成器的装饰器
    print(ret)  # ret 的值为 yield 的值:1
    ret2=b.send('eee') #把 eee 赋值给 count,并返回 count 的值 eee
    print(ret2)

    send() 与 next() 的区别

    1、如果函数内yield是表达式形式,那么必须先next(e),可以为生成器造一个装饰器让其不用每次都next一遍
    2、二者的共同之处是都可以让函数在上次暂停的位置继续运行,不一样的地方在于send在触发下一次代码的执行时,会顺便给yield传一个值
    3、send 传多个值得时候要以元祖的形式传过去

    生成器例子

    菲波那切数列

    # def fib(max):
    #     n,before,after=0,0,1
    #     while n < max:
    #         print(before)
    #         before,after=after,before+after
    #         n+=1
    #
    # fib(2)
    
    
    def fiber(max):
        n,before,after=0,0,1
        while n<max:
            yield before
            before,after=after,before+after
            n+=1
    g=fiber(8)   # 生成器对象
    # print(g)
    # print(next(g))   # 返回生成器对象的 yield 值
    # print(next(g))
    View Code

    生成器练习

    通过生成器写一个日志调用方法,支持以下功能

    根据指令向屏幕输出日志

    根据指令向文件输出日志

    根据指令同时向文件&屏幕输出日志

    以上日志格式如下

    2017-10-19 22:07:38 [1] test log db backup 3

    2017-10-19 22:07:40 [2] user alex login success

    #注意:其中[1],[2]是指自日志方法第几次调用,每调用一次输出一条日志

    def init(func):
        def warpper(*args, **kwargs):
            res = func(*args, **kwargs)
            next(res)
            return res
        return warpper
    
    
    @init
    def logger(filename, channel='terminal'):
    
        count = 1
    
        while 1:
    
            c = yield 1
            current_time = time.strftime('%Y-%m-%d %H:%M:%S')
            info = '%s [%s] %s' % (current_time, count, c,)
    
            if channel == 'terminal':
                print(info)
            elif channel == 'file':
                with open('example.ini', 'a', encoding='utf8') as f:
                    f.write('
    %s' % (info,))
            elif channel == 'both':
                print(info)
                with open('example.ini', 'a', encoding='utf8') as f:
                    f.write('
    %s' % (info,))
            count += 1
    
    
    lo = logger('abc', channel='both')
    next(lo)
    lo.send('aaa')
    time.sleep(2)
    lo.send('ccc')
    View Code

    模拟 linux tail 命令

    #!/usr/local/python3.5.2/bin/python3.5
    def tail_f(file_path):
        import time
        with open(file_path,'r') as f:
            f.seek(0,2)   # 光标始终置尾部
            while True:
                for line in f:
                    if not line:  # 如果为空就停住
                       time.sleep(0.3)
                       continue
                    else:
                        yield line.strip()
    
    g=tail_f('a.txt')
    for i in g:
        print(i)
    View Code

    模拟 linux 的 tail + grep 命令

    def tail_f(file_path):
        import time
        with open(file_path,'r') as f:
            f.seek(0,2)
            while True:
                for line in f:
                    if not line:
                       time.sleep(0.3)
                       continue
                    else:
                        yield line
    
    
    def grep(partten,lines):
        for line in lines:
            if partten in line:
                yield line
    
    g1=tail_f('a.txt')
    g2=grep('error',g1)
    for i in g2:
        print(i.strip())
    View Code

    .

  • 相关阅读:
    Mvc model验证总结
    ArchSummit全球架构师峰会2017年深圳站 漫谈
    hiredis aeStop仅在redis命令的回调函数中生效 分析
    MirrorNetwork 基于jmdns和netty的android网络通信开源库
    pigeon物联网平台- developer portal web服务设计及实现
    物联网 command 原型设计及框架
    alljoyn:基于java动态代理的RPC实现原理分析
    IOT command (based on sip)client API设计 for java
    gateway & data management
    openhab入门介绍
  • 原文地址:https://www.cnblogs.com/tootooman/p/9018215.html
Copyright © 2020-2023  润新知