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

    迭代器

    可以把迭代器比喻成一个容器,可以从这个容器中一个接一个的把值取出来,取值的过程就是可迭代的过程

      可迭代协议:含有__iter__方法

      迭代器协议:含有__iter__方法 且 __next__方法

      查看方法:print(l1.__dir__)


    可迭代对象: 含有__iter__方法 

    迭代器: 含有__iter__方法和__next__方法 迭代器.__next__ 调用迭代器


    (迭代器 是 可迭代对象 的 子集)


    第一种判断方法:

      判断是不是可迭代:print('__iter__' in dir(对象)) 对象可以是字符串、列表、字典、等等

      判断是不是迭代器:print('__next__' in dir(对象)) 

    第二种判断方法:

    from collections import Iterable
    
    from collections import Iterator
    print(isinstance(对象,Iterable))   # 判断是不是可迭代的
    print(isinstance(对象,Iterator))   # 判断是不是迭代器

    例如:

    li = []
    a = li.__iter__()
    from collections import Iterator
    from collections import Iterable
    print(isinstance([1,2],Iterable))       #从功能上判断    而type()是类型  是就是  不是就不是
    print(isinstance([1,2],Iterator))
    print(isinstance(a,Iterator))
    # True
    # False
    # True

    迭代器的特点:
      从前往后依次去取值,不可逆,不可重复
      惰性运算
      节省内存

    取值超出范围,会抛出一个异常:

    li = [1,2,3,4,5]
    li_iter = li.__iter__()
    print(li_iter.__next__())
    print(li_iter.__next__())
    print(li_iter.__next__())
    print(li_iter.__next__())
    print(li_iter.__next__())
    print(li_iter.__next__())
    #Traceback (most recent call last):
    #  File "D:/Demo/test2.py", line 26, in <module>
    #    print(li_iter.__next__())
    #StopIteration                                 超出5次  报错 StopIteration

    while 模拟 for

    li = [1,2,3,4,5]
    li_iter = li.__iter__()
    while True:
        try:
            print(li_iter.__next__())
    
        except StopIteration:          #StopIteration  报出异常
            break

    python3内置迭代器:

        内置迭代器:range() file(文件for循环读) enumerate()

    生成器

    生成器的本质: 就是 迭代器 内含__next__()方法,

    一个函数里面如果有yield,那么这个函数就是生成器函数

    def ge_iter():              #ge_iter()  就是生成器函数           生成器函数  里面有    yield关键字
        print('aa')
        yield 11        # 见到yield 取一次值,且记住这个位置,下次直接从这里开始
        print('bb')
        yield 15
        yield 1
        yield 2
    g = ge_iter()         # ge_iter()是生成器               生成器就是迭代器,能用__next__方法取值
    print(g.__next__())
    # aa
    # 11

    while取值:

    def ge_iter():
        print('aa')
        yield 11
        print('bb')
        yield 15
        yield 1
        yield 2
    g = ge_iter()           调用ge_iter()不会立即执行,而是返回一个生成器
    while True:
        try:
            print(g.__next__())
        except StopIteration:
            break

    for循环取值:

    def ge_iter():
        for i in range(10):
            yield "我是第{}名".format(i)
    g = ge_iter()
    print(g.__next__())
    print(g.__next__())
    for i in range(3):                # for循环取一段值
        print(g.__next__())      # 会接着上次保存的位置,接着取值
    # 我是第0名
    # 我是第1名
    # 我是第2名
    # 我是第3名
    # 我是第4名

    生成器惰性特点:

    def fuc1(n,i):
        return n + i
    def duoxing():
        for i in range(4):
            yield i
    g = duoxing()
    for n in [1,10]:
        g = (fuc1(n,i) for i in g)    关键点:因为生成器不取值(不打印),就永远不去运算,不运算,但for不停,
                                                所以最后 g = (fuc1(n,i) for i in (fuc1(n,i) for i in duoxing()))   
    print(list(g))                      直接把 n = 10 带入计算
    # [20, 21, 22, 23]

    生成器取值,取了就没了:

    def fuc1(n,i):
        return n + i
    def duoxing():
        for i in range(4):
            yield i
    g = duoxing()
    for n in [1,10]:
        g = (fuc1(n,i) for i in g)                                               
        print(list(g))                          第一次取值了,g生成器就没了            不管循环多少次,只有第一次有值,取空了,后面都是空列表
    
    #[1, 2, 3, 4]
    #[]

    ==

    一个生成器如下:

    def cloth():
        for i in range(100):
            yield ‘衣服%’%i
    g = cloth()

    取值:

    for i in g: 
        print(i)
    
    #等价于
    
     for i in range(100):
        print(g.__next__())  #循环触发打印100次

    for 自动触发可迭代g内部next方法 想当于第二个手动触发

    为什么用for 是因为 next() 和send()有可能遇到取完再取的报错

    而for循环取完就停止了

    示例:文件的监视,监听文件的变化

    def tail():
        f = open('文件','r',encoding='utf-8')
        f.seek(0,2)      #把光标定位到文件末尾
        while True:
            line = f.readline()  #从光标的位置读
            if line:         #line如果有内容  True
                yield line
            import time
            time.sleep(0.1)
    g = tail()
    for i in g:
        print(i.strip())
    ##注意 文件输入一定要保存 要不然显示不出来

    send()方法

    __send__() 可以写成send()
    __next__() 可以写成next()

    def f1():
        a = 1
        b = yield a   #执行到yield a 停止  返回a = 1    ***    send传值,想当于把yield a 替换 , 变成b = 5 ,继续往下走
        yield b   #定位到yield b   又停止,返回 b = 5
    g = f1()
    print(next(g))
    print(g.send(5))
    #1
    #5

    send 想当于next的同时传参( send(None) == next() ) 把生成器里面yield及后面的值整体替换成传入的参数

    send不能 用在第一个触发生成器

    next + send 数量和 = yield 数量

    预激活装饰器

    正常情况,需要用next()方法取值一次,才能用send()方法

    #每传一个数,求每次传入后的平均值
    def average():
        sum = 0
        count = 0
        aver_age = None
        while True:
            count += 1
            a = yield aver_age
            sum += a
            aver_age = sum/count
    
    g_aver = average()
    print(g_aver.__next__())
    print(g_aver.send(10))
    print(g_aver.send(20))
    print(g_aver.send(30))
    #10
    #15
    #20

    可以利用装饰器,把第一个next()方法放到装饰器里面

    def init(func):
        def inner(*args,**kwargs):
            g = func(*args,**kwargs)
            next(g)
            return g         #这个地方一定要返回一个生成器
        return inner
    @init
    def average():
        sum = 0
        count = 0
        aver_age = None
        while True:
            count += 1
            a = yield aver_age       #yield后面放什么都行,这个主要是只要一个yield,来返回aver_age 的值
            sum += a
            aver_age = sum/count
    
    g_aver = average()       #得到生成器,并赋值给g_aver,这一步不能省略,如果写成print(average.send(10)),想当于每次都重新调用average函数
    # print(g_aver.__next__())   #这一步放到装饰器函数里面,预激活
    print(g_aver.send(10))
    print(g_aver.send(20))
    print(g_aver.send(30))

    yield from   

    代替for 循环

    for i in a:

      yield i

    for i in a:
        yield i
    
    # 等价于
    
    yield from a

    示例:

    a = ‘abcd’  依次拿到‘a''b''c''d'
    def func():
        a = 'abcd'
        for i in a:    #这
            yield i     #两句
    g = func()
    print(list(g))
    # ['a', 'b', 'c', 'd']
    
    ------
    
    def func():
        a = 'abcd'
        yield from a   #这一句
    g = func()
    print(list(g))
    # ['a', 'b', 'c', 'd']
  • 相关阅读:
    判断大文件是否上传成功(一个大文件上传到ftp,判断是否上传完成)
    hbase的region
    把hdfs数据写入到hbase表
    eclipse和scala整合,打包配置文件及打包步骤
    sparkStreaming 读kafka的数据
    脚本put数据到hdfs
    Hive的自定义函数
    Ftp客户端需要TSL功能的文件上传
    Hive中的数据库、表、数据与HDFS的对应关系
    一文了解RPC框架原理
  • 原文地址:https://www.cnblogs.com/jin-yuana/p/10024929.html
Copyright © 2011-2022 走看看