python3--迭代器,生成器

发布时间:2018-04-03 18:46:13编辑:Run阅读(3499)

    一 迭代器

    1 什么是可迭代对象?

    字符串,列表,元组,字典,集合都可以被for循环,说明他们都是可迭代的

    怎么来证明这一点呢?

    注释:

    type只能判断是什么类型

    isintance判断方面更广,不仅能判断类型,还能判断是否可迭代

    Iterable是否为可迭代

    from collections import Iterable
    s = [1, 2, 3, 4]
    t = [5, 6, 7, 8]
    print(isinstance(s, Iterable)) 
    print(isinstance(s, Iterable))

    执行结果

    True

    True


    可迭代协议

    现在是从结果分析原因,能被for循环的就是"可迭代的",但是如果按常规想,for怎么知道谁是可迭代的呢?

    假设自己写了一个数据类型,希望这个数据类型里的东西也可以使用for被一个个的取出来,那就必须满足for的要求,这个要求就叫做"协议"

    可以被迭代要满足的要求就叫做可迭代协议,可迭代协议的定义非常简单,就是内部实现了__iter__方法

    下面来验证一下

    print(dir([1, 2]))
    print(dir((2, 3)))
    print(dir({1: 2}))
    print(dir({1, 2}))

    执行结果

    注释:

    dir打印该对象的所有操作方法

    blob.png


    可以被for循环的都是可迭代的,想要可迭代,内部必须有一个__iter__方法

    接着分析,__iter__方法做了什么事情呢?

    可迭代的:内部必须含有一个__iter__方法


    python迭代器

    什么叫做迭代器?迭代器英文意思是iterator

    可迭代对象转化成迭代器:可迭代对象.__iter__()--->迭代器

    迭代器不仅含有__iter__,还含有__next__ 遵循迭代器协议

    l1 = [1, 2, 3, 4]
    print(l1.__iter__())

    执行结果

    例1

    l1 = [1, 2, 3, 4]
    ss = l1.__iter__()
    print(ss.__next__())
    print(ss.__next__())
    print(ss.__next__())
    print(ss.__next__())

    执行结果

    1

    2

    3

    4

    注意:使用__next__方法取值,到底后,在执行会报错。

    blob.png

    for循环,能遍历一个可迭代对象,它的内部到底进行了什么?将可迭代对象转化成迭代器(可迭代对象.__iter__()),内部使用__next__方法,一个一个取值,加了异常处理功能,取值到底后自动停止


    仅含有__iter__方法的,就是可迭代对象

    包含__iter__和__next__方法的,就是迭代器


    迭代器的好处

    1 节省内存空间(一个好的程序员,考虑的是性能,迭代器)

    2 满足惰性机制

    3 不能反复取值,不可逆


    使用while循环模拟for循环

    1 转换成迭代器

    l1 = [1, 2, 3, 4, 5, 6, 7]

    l2 = l1.__iter__()


    2 使用__next__

    while True:

        i = l2.__next__()

        print(i)


    3 异常处理

    while True:

        try:

            i = l2.__next__()

            print(i)

        except Exception:

            break


    整个代码为

    l1 = [1, 2, 3, 4, 5, 6, 7]
    l2 = l1.__iter__()
    
    while True:
        try:
            i = l2.__next__()
            print(i)
        except Exception:
            break

    执行结果

    1

    2

    3

    4

    5

    6

    7

    try里面的代码,出现报错不会出红

    excetp表示报错的时候,该怎么处理


    生成器

    初始生成器

    我们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象通过执行iter方法得到的,迭代器有的好处是节省内存

    如果在某些情况下,我们也需要节省内存,就只能自己写,我们自己写的这个能实现迭代器功能的东西叫生成器

    Python中提供的生成器:

    1 生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果,yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行

    2 生成器表达式:类似于列表推导,但是,生成器返回按需求生产结果的一个对象,而不是一次构建一个结果列表

    生成器Generator:

        本质:迭代器(所以自带了__iter__方法和__next__方法,不需要我们去实现)

        特点:惰性运算,开发者自定义


    生成器函数

    一个包含yield关键字的函数就是一个生成器函数,yield可以为我们从函数中返回值,但是yield又不同于return,return的执行意味着程序的结束,调用生成器函数不会得到返回的具体的值,而是得到一个可迭代的对象,每一次获取这个可迭代对象的值,就能推动函数的执行,获取新的返回值,直到函数执行结束。

    生成器需要自己构建

    1 生成器函数构造

    2 生成器推导式构造

    3 数据类型的转化

    例1

    def func1():
        print(111)
        print(222)
        print(333)
        yield 666
    
    g = func1()
    print(g)

    执行结果,没有打印出值,why?

    <generator object func1 at 0x000002D2A8D033B8>

    上例中g为生成器对象

    第一:函数中只要有yield那他就不是一个函数,而是一个生成器

    第二:g称作生成器对象

    而生成器本质上也是迭代器,也可以使用__next__取值,而一个__next__对应一个yield

    def func1():
        print(111)
        print(222)
        print(333)
        yield 666
    
    g = func1()
    print(g)
    print(g.__next__())

    执行结果

    <generator object func1 at 0x000001DAB83133B8>

    111 #print打印

    222 #print打印

    333 #print打印

    666 yield __next__实际上只是打印这个666


    例2

    import time
    def genrator_fun():
        a = 1
        print('现在定义了a变量')
        yield a
        b = 2
        print('现在又定义了b变量')
        yield b
    
    gl = genrator_fun()
    print('g1:', gl)   # 打印gl可以发现gl就是一个生成器
    print('-'*10)      # 分割线
    print(next(gl))
    time.sleep(1)      # 延迟1秒
    print(next(gl))

    执行结果

    blob.png


    生成器有什么好处呢?就是不会一下子在内存中生产太多数据

    例3

    def produce():
        '''生产衣服'''
        for i in range(1000):
            yield '生产了第{}件衣服'.format(i)
    
    tmp = produce()
    print(tmp.__next__())
    print(tmp.__next__())
    print(tmp.__next__())
    num = 0
    for i in tmp:
        print(i)
        num += 1
        if num == 5:
            break

    执行结果

    blob.png

    上面的例子,也可以使用

    for i in range(1,5):

        print(tmp.__next__())

    __next__一次,yield就执行一次

    对于列表而言,for循环是从开始

    对于生成器而言,它是由指针的,__next__一次,指针向前一次,它不能从头开始,不可逆

    生成器和迭代器的区别

    迭代器:有内置方法

    生成器:开发者自定义


    send使用方法

    def generator():
        print(123)
        yield 1
        print(456)
        yield 2
    g = generator()
    g.__next__()
    g.send('hello')

    执行结果

    123

    456

    可以看到send定义的变量没有被执行输出

    next和send功能一样,都是执行一次

    第一个取值,只能用next

    send是给上一个yield发送值

    最后一个yeild是取不到值的

    def generator():
        print(123)
        content = yield 1
        print(content)
        print(456)
        yield 2
    g = generator()
    g.__next__()
    g.send('hello')

    执行结果

    123

    hello

    456


关键字