[Python核心技术与实战学习] 10 迭代器和生成器

 

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

容器:容器是一系列元素的集合,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'>

16846478-734ae50ff19708bf.png

容器、 可迭代对象和迭代器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)))

16846478-4413ab5919c98d7c.png

容器、 可迭代对象和迭代器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)

16846478-8924f1fcf2ecde5a.png

生成器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]))

16846478-11846c53ecb47544.png

双指针.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]))

16846478-fa97e38b842fa95b.png

迭代器和生成器.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]))

16846478-c5aee431360e9bcd.png

迭代器和生成器.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/

欢迎大家来一起交流学习

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
【为什么学PythonPython 是当今非常热门的语言之一,2020年的 TIOBE 编程语言排行榜Python名列第一,并且其流行度依然处在上升势头。 在2015年的时候,在网上还经常看到学Python还是学R的讨论,那时候老齐就选择了Python,并且开始着手出版《跟老齐学Python》。时至今日,已经无需争论。Python给我们带来的,不仅仅是项目上的收益,我们更可以从它“开放、简洁”哲学观念得到技术发展路线的启示。 借此机会,老齐联合CSDN推出了本课程,希望能影响更多的人走进Python,踏入编程的大门。 【课程设计】 本课程共包含三大模块: 一、基础知识篇 内置对象和基本的运算、语句,是Python语言的基础。本课程在讲解这部分知识的时候,不是简单地将各种知识做简单的堆砌,而是在兼顾内容的全面性的同时,更重视向学习者讲授掌握有关知识的方法,比如引导学习者如何排查错误、如何查看和理解文档等。   二、面向对象篇 “面向对象(OOP)”是目前企业开发主流的开发方式,本课程从一开始就渗透这种思想,并且在“函数”和“类”的学习强化面向对象开发方式的学习——这是本课程与一般课程的重要区别,一般的课程只在“类”这里才提到“对象”,会导致学习者茫然失措,并生畏惧,乃至于放弃学习。本课程则是从开始以“润物细无声”的方式,渗透对象概念,等学习到本部分的时候,OOP对学习者而言有一种“水到渠成”的感觉。   三、工具实战篇 在项目实战,除了前述的知识之外,还会用到很多其他工具,至于那些工具如何安装?怎么自己做工具?有那些典型工具?都是这部分的内容。具体来说,就是要在这部分介绍Python标准库的应用以及第三方包的安装,还有如何开发和发布自己的工具包。此外,很多学习Python的同学,未来要么从事数据科学、要么从事Web开发,不论哪个方向,都离不开对数据库的操作,本部分还会从实战的角度,介绍如何用Python语言操作常用数据库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值