• python基础 生成器 迭代器


    列表生成式:

    a=[1,2,3]
    print a
    b=[i*2 for i in range(10)]    #i循环10次,每一个i的值乘2就是列表中的值。列表生成式
    print b
    >>[1, 2, 3]
    >>[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
    

     生成器:

    通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

    所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

    b=(i*2 for i in range(10))  #将生成列表中的[]转换成(),就是一个生成器。
    print b
    

     生成器与列表的区别:

    列表在定义的时候,已经在内存中开辟一定空间,而生成器只有在访问元素时才会在内存中开辟空间。

    a=[1,2,3]     #列表
    print a
    b=(i*2 for i in range(10))    #生成器
    print b  
    >>[1, 2, 3]      #内存中的值
    >><generator object <genexpr> at 0x025048C8>    #内存地址
    b=(i*2 for i in range(10)) 
    for i in b:       #生成器只有在访问元素时才会在内存中开辟空间
      print i
    print b
    0
    2
    4
    6
    8
    10
    12
    14
    16
    18
    <generator object <genexpr> at 0x02154AF8>
    

     

    注:生成器不能通过像使用列表那样去使用切片、通过索引访问元素值。

    只能通过for循环一个个的取值(只有在调用时才会生成相应的数据),生成器只记住当前的位置,当前的值,前面已经输出的值,生成器不会记得,只有一个_next_()方法3.0,2.7next()

    斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

    def fib(max):
        n, a, b = 0, 0, 1
        while n < max:
            print(b)
            a, b = b, a + b
            n = n + 1
        return 'done'
    fib(10)
    
    1
    1
    2
    3
    5
    8
    13
    21
    34
    55
    done
    

      

    def fib(max):
        n,a,b = 0,0,1
    
        while n < max:
            #print(b)
            yield  b     '''上面的函数和generator仅一步之遥。要把fib函数变成generator, 只需要把print(b)改为yield b ,yidld 也有return的作用'''
            a,b = b,a+b
            n += 1
        return 'done' 
     f = fib(6)
     f
    data = fib(10)
    print(data)
    print(data.__next__())
    print(data.__next__())
    print("ss")
    print(data.__next__())
    print(data.__next__())
    print(data.__next__())
    >><generator object fib at 0x104feaaa0>
    >><generator object fib at 0x101be02b0>
    >>1
    >>1
    >>ss
    >>2
    >>3
    >>5
    

    还可通过yield实现在单线程的情况下实现并发运算的效果(准确说是协程)

    import time
    def consumer(name):
        print("%s 准备吃包子啦!" %name)
        while True:
           baozi = yield
    
           print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
    def producer(name):
        c = consumer('A')
        c2 = consumer('B')
        print c          
        c.next()       #这里的c指的是在内存中存放A的内存地址    0x02265490
        c2.next()
        print("老子开始准备做包子啦!")
        for i in range(5):
            time.sleep(1)
            print("做了2个包子!")
            c.send(i)
            c2.send(i)
    producer("小明")
    
    <generator object consumer at 0x021D5490>
    A 准备吃包子啦!
    B 准备吃包子啦!
    老子开始准备做包子啦!
    做了2个包子!
    包子[0]来了,被[A]吃了!
    包子[0]来了,被[B]吃了!
    做了2个包子!
    包子[1]来了,被[A]吃了!
    包子[1]来了,被[B]吃了!
    做了2个包子!
    包子[2]来了,被[A]吃了!
    包子[2]来了,被[B]吃了!
    做了2个包子!
    包子[3]来了,被[A]吃了!
    包子[3]来了,被[B]吃了!
    做了2个包子!
    包子[4]来了,被[A]吃了!
    包子[4]来了,被[B]吃了!
    

    yield 返回当前函数在内存中的地址,

    next(),回到yield的执行位置,没有传值的作用。

    send(b1) 回到yield的执行位置,可以传值给yield。

     迭代器

    迭代,顾名思义就是重复做一些事很多次,迭代器是实现了__next__()方法的对象。

    能for循环的对象统称为可迭代对象:Iterable,可以使用isinstance()判断一个对象是否是Iterable对象:

    from collections import Iteable
    isinstance([],Iterable)
    
    true
    

    可以被next()调用并不断返回下一个值的对象成为迭代器Iterator,查看对象是否是迭代器使用dir()查看是否用next()函数。

    生成器就是一个迭代器,

    判断是不是一个迭代器:

    from collections import Iterator
    isinstance((i for i in range(5)),Iterator)
    true
    

    生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

    listdictstrIterable变成Iterator可以使用iter()函数:

    >>> isinstance(iter([]), Iterator)
    True
    >>> isinstance(iter('abc'), Iterator)
    True
    

      

      

     

     

     

  • 相关阅读:
    VIM文本替换命令
    VIM格式化代码(How to format code with VIM)
    字符串匹配的Boyer-Moore算法
    Java中数组的遍历
    UVa10723
    uva242,Stamps and Envelope Size
    UVa1630,Folding
    uva1629,Cake Slicing,记忆化搜索
    uva 10118,记忆化搜索
    uva10003
  • 原文地址:https://www.cnblogs.com/iexperience/p/9053918.html
Copyright © 2020-2023  润新知