Python核心技术与实战
景霄
Facebook资深工程师
立即订阅
13891 人已学习
课程目录
已完结 46 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 从工程的角度深入理解Python
免费
基础篇 (14讲)
01 | 如何逐步突破,成为Python高手?
02 | Jupyter Notebook为什么是现代Python的必学技术?
03 | 列表和元组,到底用哪一个?
04 | 字典、集合,你真的了解吗?
05 | 深入浅出字符串
06 | Python “黑箱”:输入与输出
07 | 修炼基本功:条件与循环
08 | 异常处理:如何提高程序的稳定性?
09 | 不可或缺的自定义函数
10 | 简约不简单的匿名函数
11 | 面向对象(上):从生活中的类比说起
12 | 面向对象(下):如何实现一个搜索引擎?
13 | 搭建积木:Python 模块化
14 | 答疑(一):列表和元组的内部实现是怎样的?
进阶篇 (11讲)
15 | Python对象的比较、拷贝
16 | 值传递,引用传递or其他,Python里参数是如何传递的?
17 | 强大的装饰器
18 | metaclass,是潘多拉魔盒还是阿拉丁神灯?
19 | 深入理解迭代器和生成器
20 | 揭秘 Python 协程
21 | Python并发编程之Futures
22 | 并发编程之Asyncio
23 | 你真的懂Python GIL(全局解释器锁)吗?
24 | 带你解析 Python 垃圾回收机制
25 | 答疑(二):GIL与多线程是什么关系呢?
规范篇 (7讲)
26 | 活都来不及干了,还有空注意代码风格?!
27 | 学会合理分解代码,提高代码可读性
28 | 如何合理利用assert?
29 | 巧用上下文管理器和With语句精简代码
30 | 真的有必要写单元测试吗?
31 | pdb & cProfile:调试和性能分析的法宝
32 | 答疑(三):如何选择合适的异常处理方式?
量化交易实战篇 (8讲)
33 | 带你初探量化世界
免费
34 | RESTful & Socket: 搭建交易执行层核心
35 | RESTful & Socket: 行情数据对接和抓取
36 | Pandas & Numpy: 策略与回测系统
免费
37 | Kafka & ZMQ:自动化交易流水线
38 | MySQL:日志和数据存储系统
39 | Django:搭建监控平台
40 | 总结:Python中的数据结构与算法全景
技术见闻与分享 (4讲)
41 | 硅谷一线互联网公司的工作体验
42 | 细数技术研发的注意事项
加餐 | 带你上手SWIG:一份清晰好用的SWIG编程实践指南
43 | Q&A:聊一聊职业发展和选择
结束语 (1讲)
结束语 | 技术之外的几点成长建议
Python核心技术与实战
登录|注册

19 | 深入理解迭代器和生成器

景霄 2019-06-21
你好,我是景霄。
在第一次接触 Python 的时候,你可能写过类似 for i in [2, 3, 5, 7, 11, 13]: print(i) 这样的语句。for in 语句理解起来很直观形象,比起 C++ 和 java 早期的 for (int i = 0; i < n; i ++) printf("%d\n", a[i]) 这样的语句,不知道简洁清晰到哪里去了。
但是,你想过 Python 在处理 for in 语句的时候,具体发生了什么吗?什么样的对象可以被 for in 来枚举呢?
这一节课,我们深入到 Python 的容器类型实现底层去走走,了解一种叫做迭代器和生成器的东西。

你肯定用过的容器、可迭代对象和迭代器

容器这个概念非常好理解。我们说过,在 Python 中一切皆对象,对象的抽象就是类,而对象的集合就是容器。
列表(list: [0, 1, 2]),元组(tuple: (0, 1, 2)),字典(dict: {0:0, 1:1, 2:2}),集合(set: set([0, 1, 2]))都是容器。对于容器,你可以很直观地想象成多个元素在一起的单元;而不同容器的区别,正是在于内部数据结构的实现方法。然后,你就可以针对不同场景,选择不同时间和空间复杂度的容器。
所有的容器都是可迭代的(iterable)。这里的迭代,和枚举不完全一样。迭代可以想象成是你去买苹果,卖家并不告诉你他有多少库存。这样,每次你都需要告诉卖家,你要一个苹果,然后卖家采取行为:要么给你拿一个苹果;要么告诉你,苹果已经卖完了。你并不需要知道,卖家在仓库是怎么摆放苹果的。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Python核心技术与实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(70)

  • John Si 置顶
    我不知道如何把这技巧运用在编程中,老师能否举几个例子来说明一下呢?谢谢

    作者回复: 例子已经在文中举了不少,对于如何娴熟地在编程中运用,这个需要长时间的积累,从阅读别人高质量的源代码,自己主动有意识地在自己的项目中思考,最后才会形成质变,内化成自己的能力,从而清楚地知道哪里应该用高级语法,高级工具,哪里应该简单的一笔带过。Python 的生成器无疑是最有用的特性,但也是最不广泛被使用的特性,这一章的目的,能够让你对生成器有基本的了解,下次在代码中遇到,能够说,“这个我知道,这个我懂!”便已足够。加油!

    2019-06-21
    1
    12
  • Jingxiao 置顶
    思考题答案:
    很多同学的回复非常正确,生成器只能遍历一次,继续调用 next() 会 raise StopIteration。只有复位生成器才能重新进行遍历。
    2019-06-23
    10
  • 时间小偷
    上一篇的分享mateclass写得看不懂,老师可否重新通俗写一下,分享嘉宾的风格跟老师不太一样啊
    2019-06-21
    44
  • Destroy、
    def is_subsequence(a, b):
        b = iter(b)
        print(b)

        gen = (i for i in 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(is_subsequence([1, 4, 3], [1, 2, 3, 4, 5]))

    ########## 输出 ##########

    <list_iterator object at 0x000001E7063D0E80>
    <generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C570>
    1
    3
    5
    <generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C5E8>
    True
    True
    True
    False
    <list_iterator object at 0x000001E7063D0D30>
    <generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C5E8>
    1
    4
    3
    <generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C570>
    True
    True
    False
    False

    为什么这里的print(is_subsequence([1, 3, 5], [1, 2, 3, 4, 5]))会返回False?
    解释一下:
    因为
        gen = ((i in b) for i in a)
        print(gen)
        
        for i in gen:
            print(i)
    这段代码的 for i in gen 已经b的迭代器消耗完,此时的b已经是个空的迭代器。所以,再执行all(((i in b) for i in a)),就是False了。

    作者回复: 解释的很好

    2019-06-21
    1
    15
  • SCAR
    思考题:对于一个有限元素的生成器,如果迭代完成后,继续调用 next(),会跳出StopIteration:。生成器可以遍历多次吗?不行。也正是这个原因,老师代码复杂化那段代码,在
        gen = ((i in b) for i in a)
        for i in gen:
            print(i)
    之后应该是需要给b复位下,不然b会是空的,那么return回来永远会是False。

    这段判断是否是子序列的指针用的真是巧妙,区区几行,精华尽现。

    作者回复: 👍

    2019-06-21
    14
  • farFlight
    迭代完成后,继续调用 next()会出现StopIteration。
    生成器只能遍历一次,但是可以重新调用重新遍历。

    作者回复: 正确

    2019-06-21
    6
  • tt
    明白为啥要把b转换成迭代器了,是为了下面的代码中可以用next():

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

    这样才可以利用next()可以保存指针位置的特性,从而确保子序列中元素的顺序。

    作者回复: 对,这里是个很巧妙的利用

    2019-06-21
    5
  • xiemanrui
    def is_subsequence(a, b):
        b = iter(b)
        print(b)

        gen = (i for i in 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(is_subsequence([1, 4, 3], [1, 2, 3, 4, 5]))

    这个代码很容易让人误解,is_subsequence的返回结果永远是False的,因为迭代器b被用了两次了。
    2019-06-29
    3
  • Element 静婷
    老师好,请问子序列的问题中,[1,3,5]不是[1, 2, 3, 4, 5]吗?怎么返回false
    2019-06-25
    2
  • Wing·三金
    思考题:其实开头就已经明示了答案,会出现 StopIteration Error。遍历是一次性,参考下面这段代码:

    def index_generator(L, target):
        for i, num in enumerate(L):
            if num == target:
                yield i

    result = index_generator([1, 6, 2, 4, 5, 23, 4, 54, 34, 3, 2], 2)
    print(list(result))
    print(list(result))

    ### output
    [2, 10]
    []

    补充一小点:上面的 isinstance(obj, Iterable) 中的 Iterable 需要先 from collections import Iterable。

    另外有个问题,py3 中的 range() 本质上也是 generator 吗?如果是,为何下面这段代码的结果会是这样呢?

    e = range(3)

    for i in e:
        print(i)
    for i in e:
        print(i)

    ### expected
    0
    1
    2
    ### real output
    0
    1
    2
    0
    1
    2

    请各位指教~!
    2019-06-22
    1
    2
  • 許敲敲
    b = (i for i in range(5))

    print(2 in b)
    print(4 in b)
    print(3 in b)

    ########## 输出 ##########

    True
    True
    False

    这里面的判断4 in b后,指针已经在3 之后了吗?所以 3 in b 会返回 false

    反过来 如果
    b = (i for i in range(5))

    print(2 in b)
    print(3 in b)
    print(4 in b)

    ########## 输出 ##########

    True
    True
    True

    这么理解对吗?

    2019-06-22
    2
  • Redevil
    前三个布尔值打印的是a的三个元素在不在b中的判断结果
    第四个值是打印is_subsequence的最终返回值
    2019-06-21
    2
  • Hurt
    老师并没讲迭代器和可迭代对象区别,另外还有他们底层的实现,魔法方法
    2019-06-21
    1
    2
  • kyle
    gen = ((i in b) for i in a)
    实际上是先遍历 a,取出一个值赋给i,然后再判断i是否在b中,判断一次,b中的指针后移一位。

    所以,第一轮的输出应该是:TRUE,TRUE,TRUE(前三个是for循环打印的,因为a中有3个元素),TRUE(最后一个是函数返回的);

    第二轮输出是:TRUE,TRUE,FALSE,FALSE
    2019-06-21
    2
  • 鱼_XueTr
    会引发StopIteration。
    生成器只能使用一次。

    作者回复: 👍

    2019-06-21
    2
  • 蒋腾飞同学
    老师好 ,生成器(i for i in range(5))和tuple数据很像,都是可迭代的,请问有什么本质区别吗?都是小括号扩起来~
    2019-09-23
    1
  • 佛本是道
    生成器复位怎么操作?
    2019-07-19
    1
  • 超人
    看完文章后一直不明白为什么输出的是
    true,true,true,false
    true,true,false,false,以及最后的例子输出true,true,false
    看完看完评论后搞懂了,关键在下面代码
    while True:
        val = next(b)
        if val == i:
            yield True

    1,4,3列表,在判断4时,指针指向迭代器4的位置,判断3时,next是5,所以返回false



    2019-06-26
    1
  • Monroe He
    列表元组字典集合都是可迭代对象,它们可以通过 iter() 函数返回一个迭代器,再通过next()函数就可以实现遍历,for in语句将整个过程隐式化。
    gen = ((i in b) for i in a)
    print(gen)
    for i in gen:
        print(i)
    先得到a中的一个元素i; 迭代b,检查i是否与b当前位置相等,如果i与b当前位置元素相等,则返回True,b继续停留在当前位置,等待下次从当前位置开始迭代。

    问题:b是一个迭代器,如果在之前不将b转换为迭代器,而保持b为可迭代对象(也就是列表)为什么不行。因为之前说过for in 语句会隐式化地执行iter()函数,并利用next()函数进行遍历。既然可以隐式化的执行,为什么要将列表转为迭代器呢?
    2019-06-24
    1
  • Geek_59f23e
    1、大家对next函数可能有些误区,迭代完成后继续调用next函数会返回默认值None。
     iterator.__next__() 方法和 next(iterator, default=None) 函数的区别在于:前者迭代完成后会抛出StopIteration错误,中断程序运行,而后者会返回一个默认值None(可以指定),不会报错和中断程序运行。

    2、生成器遍历到最后一个元素后抛出StopIteration,不能遍历多次,重新遍历需要生成一个新的生成器。

    作者回复: 👍

    2019-06-22
    1
收起评论
70
返回
顶部