• Python之迭代器 生成器


    迭代器

    1. 可迭代协议和迭代器协议

      迭代器:Iterator      iterable :可迭代的

      可迭代协议:只要对象中包含了__iter__方法,就是可迭代的

      迭代器协议:迭代器中有__next__和__iter__方法

      迭代器实现了能从中一个一个的取值

     查看是否可迭代

    from collections.abcimport Iterable    # Iterable检测一个对象是否可迭代
    print(isinstance(对象,数据类型/iterable))     #检测对象是否是该数据类型或者是否可迭代
    
    print(dir(对象))    # 可以查看对象可以使用的功能
    
    print(set(dir(对象))-set(dir(对象)))    # 可以求这两个对象之间可使用的功能的差集

     查看是否是迭代器

    from collections.abc import Iterator    # Iterator判断是否是迭代器
    
    print(isinstance([1,2,3],Iterator))
    

    2. 迭代器

    列表本身是可迭代的 但不是迭代器
    from collections.abc import Iterable
    from collections.abc import Iterator    # 注:python3.7中collections 模块弃用 需从collections.abc模块进行导入
    
    
    li = [1, 2, 3]
    
    print(isinstance(li, Iterator))     # False  
    print(isinstance(li, Iterable))     # True
    

     列表怎么变成迭代器?

    from collections.abc import Iterator
    
    
    lst_iterator = [1,2,3].__iter__()    # 调用__iter__()方法
    
    print(isinstance(lst_iterator, Iterator))   # True
    

     Iter() 与 __iter__() 用于产生 iterator(迭代器)
      __iter__ 迭代器协议
        凡是实现__iter__协议的对象,皆是迭代器对象。(next()也得实现,不然没法产生数据)
      Iter()迭代器工厂函数
        凡是有定义有__iter__()函数,或者支持序列访问协议,也就是定义有__getitem__()函数的对象 皆可以通过 iter()工厂函数 产生迭代器(iterable)对象

    3. 迭代器的取值

    lst_iterator = [1,2,3].__iter__()
    
    print(lst_iterator.__next__())
    print(lst_iterator.__next__())
    print(lst_iterator.__next__())
    # 超出范围后报错 StopIteration
    

    4. 可迭代对象 迭代器小结

      a. Python语言中可以用for循环遍历的,都是可迭代对象

      b. 可迭代对象包含迭代器

      c. 迭代器的本质:能够对python中的数据类型进行统一的遍历,不需要关心每一个值是什么

      d. 迭代器属于惰性运算,可以节省内存空间

    5.  迭代器有两种:

      天生的:文件句柄

      后天的:可迭代对象.__iter__()

      文件句柄是一个迭代器,range是一个可迭代对象

    生成器

    1. Gerator   本质就是迭代器,生成器是自己实现

      生成器有两种:生成器函数和生成器表达式

    2. 生成器函数

      a. 生成器函数在执行的的时候返回一个生成器

      b. 调用生成器函数时,第一次都是不不执行,而是获取生成器对象

      c. 生成器只能向下取值,不能往回找

      d. 生成器只有在取值的时候,才会执行

    def generator_func():
        print(123)
        yield "aaa"
        print(456)
        yield  "bbb"
    
    # 需要先获取生成器 g = generator_func() # g 就是生成器对象 这一步的执行并不会真的执行generator_func 而是由generator_func返回一个生成器对象 print(g) # <generator object generator_func at 0x0000019899601B88> # 使用__next__()方法取值 ret = g.__next__() # ret 是第一个yield返回的值 print(ret) ret1 = g.__next__() print(ret1) # 使用for.. in .. 取值 for i in g: print("i:",i) # 每遍历一次 返回一个yield的值

    3. yield关键字

      a. 带yield的就是生成器函数

      b. yield不会终止函数

      c. yield后面不加的时候就会是返回None

    4.  从生成器取值:

      a. __next__ 有几个yield就可以取几次

      b. for循环取值 正常取  for item in generator:

      c. 其他数据类型强制转换 list(generator) 返回一个列表,里面放着生成器的所有内容

    # 列表取值
    def generator_func(): print(123) yield "aaa" print(456) yield "bbb" g = generator_func() print(list(g)) # ['aaa', 'bbb']

      

    def generator_func():
        print(123)
        yield "aaa"    
        print(456)
        yield "bbb"     
    
    # __next__()取值
    generator_func().__next__()     # 直接调用不可以,相当于每次都调用一个新的生成器
    print(generator_func().__next__)    # aaa
    
    generator_func().__next__()     # 直接调用不可以,相当于每次都调用一个新的生成器
    print(generator_func().__next__)    # aaa
    
    # for循环取值
    for i in generator_func():      # for 的时候可以直接遍历,因为只调用了一个生成器
        print("i:",i)
    
    # for和list取值混用
    
    def func():
        yield 1
        yield 2
    
    g = func()
    for i in g:
        print(list(g))      # 这里只会打印一个[2],因为前面for循环的时候就已经拿到了一个1,现在去list(g)只能取到后面的2
    

      三个取值方法小结:最好使用for循环取值,list强转取值法不推荐,因为无法知道生成器中的数据有多少,双下next取值方法不是很常用,并且用起来比较麻烦。

    5. yield from

      yield from 后边需要跟可迭代对象

    def func():
        yield from [1,2,3]
        print('=============')
        yield from "adfd"
    
    
    for i in func():
        print(i)
    
    # 结果 会按顺序依次拿到可迭代对象中的元素
    '''
    1
    2
    3
    =============
    a
    d
    f
    d
    '''
    

    6. send关键字

      a. send 是往里面传进去参数值,send不返回值的时候和next是一样的,在生成器执行伊始,只能先用next

      b. send传递参数的时候,生成器中必须要有一个未被返回的yield

      c. 函数里有几个yield就有几个next,并且第一个next不能改变,后面的可以改为send,send和text的总和就是yield的个数

    def func():
        print(123)
        value = yield 1   # next就是执行到yield 1,当执行send的时候,就从value = send传进来的值开始执行
        print(value)
        print(456)
        yield "***"+value+"***"
    
    g = func()
    print(g.__next__())     # 推动func的运行,打印123,直到yield返回一个1,就暂停了
    print(g.send("aaa"))    # 接着执行send的时候,把"aaa"传进去,赋值给value后,接着向下执行,打印456,***aaa***
    
    def func():
        print(1)
        yield 2     # next的时候就实行到这里,当send88进来的时候,并没有变量去接受88,,
        print(3)
        value = yield 4     # 在这里,并没有send传值进来,所以value打印出来是空的
        print(5)
        yield value
    
    g = func()
    print(g.__next__())    # 2
    print(g.send(88))  # 4
    print(g.__next__())    # None
    

    7. 带装饰器的生成器

    def wrapper(func):    # 生成器预激装饰器
        def inner(*args,**kwargs):
            g = func(*args,**kwargs)    # g就是一个生成器
            g.__next__()    # 激活生成器
            return g
        return inner
    
    @wrapper
    def average_func():
        total = 0
        count = 0
        average = 0
        while True:
            value = yield average
            total += value
            count += 1
            average = total/count
    
    
    g = average_func()
    print(g.send(30))
    

    8. 列表表达式,列表推导式

    # /的结果是浮点数
    # 两个//数字就不是带小数点的
    
    new_l = [i*i for i in [1,3,5]]
    print(new_l)
    
    print([i//2 for i in range(0,7,2)])
    

    9. 生成器表达式

    a = ("egg%d"%i for i in range(10))
    
    # 三种方法从生成器取值:
    
    # 1. __next()
    print(a.__next__())
    
    # 2. for 
    for i in a:
        print(i)
    
    # 3.  list强转
    print(list(a))
    

    10.  字典推导式

    # 将一个字典的key和value值对换
    
    mcase = {"a":10,"b":20}
    new_dic={mcase[k]:k for k in mcase}
    print(new_dic)
    

    11. 集合推导式 (可以去重)

    new_l = {i*i for i in [1,3,5]}
    print(new_l)
    

    12. 示例

      a. Python代码和列表表达式比较

    l = [{'name':'alex','age':80},{'name':'egon','age':40},{'name':'yuan','age':30},{'name':'nezha','age':18}]
    new_l = []
    for d in l:
        new_l.append(d["name"])
    print(new_l)
    
    l = [{'name':'alex','age':80},{'name':'egon','age':40},{'name':'yuan','age':30},{'name':'nezha','age':18}]
    print([i["name"] for i in l])
    print([i["name"] for i in l if i["age"] > 18])
    

       b. 生成器表达式

    # 30以内能被3整除的数
    print([i for i in range(30) if i%3==0])
    
    # 30以内能被3整除的数的平方
    print([i*i for i in range(30) if i%3==0])
    

       c. 生成器取值方法 for list 混用

    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))    # 把g1里的值取出来之后,g1就是空的了
    print(list(g2))    # g2就是空的
    # 结果
    # [0, 1, 2, 3]
    # []
    

       d. 生成器的坑

    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)
    
    
    print(list(g))  # [15, 16, 17, 18]

     解析:

      为什么结果是 [15, 16, 17, 18]???

      按正常逻辑来说,执行过程如下:
      1. n=1 for i in g: i的值 分别是 0 1 2 3,执行完add(n, i), g=(1, 2, 3, 4)
      2. n=10 for i in g: i的值 分别是 1, 2, 3, 4,执行完add(n, i), g=(11, 12, 13, 14)
      3. n=5 for i in g: i的值 分别是 11, 12, 13, 14,执行完add(n, i), g=(16, 17, 18, 19)

      需要注意的点是,for循环中的g是生成器表达式,如果换做列表表达式,那上边的过程就是对的。对于生成器需要注意的点就是,生成器只有在取值的时候才会执行。

      所以我们在最后调用list方法取值时,生成器g还未执行,而此时for循环已经执行完毕,n在内存中最后的值是5。此时 g = (add(n,i) for i in g),这里边的 g 和我们现在找的g 是一样的,所以最终的生成器是 g = (add(n,i) for i in g = (add(n,i) for i in g = (add(n,i) for i in test()))) 。计算结果即为 [15, 16, 17, 18]

    小结:

      1. 最常用的是列表推导式, 在工作中,尽量把列表变成生成器表达式。
      2. 尽量让推导式简化操作,增强代码的可读性,如果推导式过于复杂,应该转换成普通的python代码。
      3. 所有的列表推导式都可以转换成生成器表达式,应该多使用生成器表达式,少使用列表表达式。
      4. 在代码里,多层嵌套for循环是禁忌,会大幅度增加代码的复杂度。

  • 相关阅读:
    (转)交换两个变量的值,不使用第三个变量的四种法方
    Java权威编码规范
    Git的安装和设置
    ActiveMQ简单入门
    JMS术语
    求助 WPF ListViewItem样式问题
    初步探讨WPF的ListView控件(涉及模板、查找子控件)
    圆角button
    用Inno Setup来解决.NetFramework安装问题
    [!!!!!]Inno Setup教程-常见问题解答
  • 原文地址:https://www.cnblogs.com/chitalu/p/11433606.html
Copyright © 2020-2023  润新知