Python 基础进阶学习

10 迭代器和生成器

2019-07-11  本文已影响0人  leacoder

目录链接: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/

欢迎大家来一起交流学习

上一篇 下一篇

猜你喜欢

热点阅读