10 迭代器和生成器

作者: leacoder | 来源:发表于2019-07-11 23:39 被阅读0次

    目录链接:https://www.jianshu.com/p/e1e201bea601

    容器、 可迭代对象和迭代器

    容器:容器是一系列元素的集合,str、list、set、dict、file、sockets对象都可以看作是容器,容器都可以被迭代(用在for,while等语句中),因此他们被称为可迭代对象。

    可迭代对象:很多容器都是可迭代对象,此外还有更多的对象同样也是可迭代对象,比如处于打开状态的files,sockets等。但凡是可以返回一个迭代器的对象,都可称之为可迭代对象。

    迭代器:一个带状态的对象,能在你调用next()方法时,返回容器中的下一个值。任何实现了iter()和next()方法的对象都是迭代器,iter返回迭代器自身,next返回容器中的下一个值,如果容器中没有更多元素了,则抛出StopIteration异常。

    x = [1, 2, 3]
    
    y = iter(x)
    z = iter(x)
    
    print(next(y))            # 1
    print(next(y))            # 2
    print(next(z))            # 1
    print(type(x))            # <class 'list'>
    print(type(y))            # <class 'list_iterator'>
    
    容器、 可迭代对象和迭代器01.png

    x是一个可迭代对象,y和z是两个独立的迭代器。可迭代对象, 通过 iter() 函数返回一个迭代器, 再通过 next() 函数就可以实现遍历。

    下面这段代码展示怎么判断一个对象是否可迭代,当然也可以用isinstance(obj, Iterable)

    from collections import Iterable
    
    def is_iterable(param):
        try: 
            iter(param) 
            return True
        except TypeError:
            return False
    
    params = [
        1234,
        '1234',
        [1, 2, 3, 4],
        set([1, 2, 3, 4]),
        {1:1, 2:2, 3:3, 4:4},
        (1, 2, 3, 4)
    ]
        
    for param in params:
        print('Method 1: {} is iterable? {}'.format(param, is_iterable(param)))
    print("--------------------------------------------------------------------")
    for param in params:
        print('Method 2: {} is iterable? {}'.format(param, isinstance(param,Iterable)))
    
    容器、 可迭代对象和迭代器01.png

    生成器

    生成器是一种特殊的迭代器。

    一个函数只返回一次,但一个生成器能暂停执行并返回一个中间的结果(yiled关键字),当生成器的next()方法被调用时,它就又会从离开的地方继续运行,实现一边循环一边计算的机制,这种就称为生成器generator。

    def generator(k):
        i = 1
        while True:
            yield i ** k
            i += 1
    
    gen_1 = generator(1)  # 返回一个生成器
    gen_3 = generator(3)
    print(gen_1)
    print(gen_3)
    
    def get_sum(n):
        sum_1, sum_3 = 0, 0
        for i in range(n):
            next_1 = next(gen_1)
            next_3 = next(gen_3)
            print('next_1 = {}, next_3 = {}'.format(next_1, next_3))
            sum_1 += next_1
            sum_3 += next_3
        print(sum_1 * sum_1, sum_3)
    
    get_sum(8)
    
    生成器01.png

    generator() 这个函数, 它返回了一个生成器。yield 关键词,函数运行到这里的时候, 程序会从这暂停, 然后跳出, 不过跳到哪里呢?答案是 next() 函数。 那么 i ** k 是干什么的呢?它其实成了 next() 函数的返回值。

    每次 next(gen) 函数被调用的时候, 暂停的程序就又复活了, 从 yield 这里向下继续执。局部变量 i 并没有被清除掉,而是会继续累加。

    这个生成器可以一直进行下去,事实上, 迭代器是一个有限集合, 生成器则可以成为一个无限集。只管调用next(), 生成器根据运算会自动生成新的元素, 然后返回。

    例子

    判断子序列:https://leetcode-cn.com/problems/is-subsequence/

    给定字符串 s 和 t ,判断 s 是否为 t 的子序列。字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。[1, 3, 5] 是 [1, 2, 3, 4, 5] 的子序列, [1, 4, 3] 则
    不是。

    常规解法:
    双指针,指针 i 指向 s 第一个字符,指针 j 指向 t 第一个字符。逐一判断 i 所指向的字符是否在 t 中存在。

    如果 s[i] != t[j]:j = j + 1,继续比对下一个字符

    如果 s[i] == t[j]:i = i + 1,j = j + 1,继续进行下一个 s[i] 的查找

    def isSubsequence(s, t):
            s_length = len(s)
            t_length = len(t)    
            i = 0
            j = 0 
            while i < s_length and j < t_length:
                if s[i] == t[j]:
                    i += 1
                j += 1
            return i == s_length
    print(isSubsequence([1, 3, 5], [1, 2, 3, 4, 5]))
    print(isSubsequence([1, 4, 3], [1, 2, 3, 4, 5]))
    
    双指针.png

    使用迭代器和生成器:

    def is_subsequence(a, b):
        b = iter(b)  # 迭代器
        return all( i in b for i in a )
    
    print(is_subsequence([1, 3, 5], [1, 2, 3, 4, 5]))
    print(is_subsequence([1, 4, 3], [1, 2, 3, 4, 5]))
    
    迭代器和生成器.png

    先把这段代码扩展,一步一看

    def is_subsequence(a, b):
        b = iter(b)   # 把列表 b 转化成了⼀个迭代器
        print(b)
    
        gen = (i for i in a)  # 产生一个生成器, 这个生成器可以遍历对象 a
        print(gen)
    
        for i in gen:
            print(i)
    
        gen = ((i in b) for i in a)
        print(gen)
    
        for i in gen:
            print(i)
    
        return all(((i in b) for i in a))
    
    print(is_subsequence([1, 3, 5], [1, 2, 3, 4, 5]))
    print('--------------------------------------------')
    print(is_subsequence([1, 4, 3], [1, 2, 3, 4, 5]))
    
    迭代器和生成器.png

    b = iter(b) , 把列表 b 转化成了一个迭代器, gen = (i for i in a) 产生一个生成器, 这个生成器可以遍历对象 a,能够输出 1, 3, 5。(i in b)可以联想到 for in 语句。

    这里的 (i in b) , 大致等价于下面这段代码:

    while True:
        val = next(b)
        if val == i:
            yield True
    

    巧妙地利用了成器的特性, next() 函数运行的时候, 保存了当前的指针。

    那么((i in b) for i in a)可以理解为:遍历a中元素 i,并在b中查找元素 i 是否存在。由于next() 函数和yield 成器的特性,如果 b 存在 i 返回True并保存当前指针不存在返回False(下一次查找从保存的指针开始继续查找,直到a 或者 b 遍历结束),这些True或False产生一个迭代器。

    最后的 all() 函数,判断一个迭代器的元素是否全部为 True, 如果是则返回 True, 否则就返回 False。

    参考资料:

    极客时间 Python核心技术与实战学习

    Python核心技术与实战(极客时间)链接:
    http://gk.link/a/103Sv


    GitHub链接:
    https://github.com/lichangke/LeetCode

    知乎个人首页:
    https://www.zhihu.com/people/lichangke/

    简书个人首页:
    https://www.jianshu.com/u/3e95c7555dc7

    个人Blog:
    https://lichangke.github.io/

    欢迎大家来一起交流学习

    相关文章

      网友评论

        本文标题:10 迭代器和生成器

        本文链接:https://www.haomeiwen.com/subject/glgjkctx.html