流畅的python笔记(十四)可迭代的对象、迭代器和生成器

《流畅的python》读书笔记 《流畅的python》这本书值得推荐,认真读完2篇,对python的理解提高一大截。本书属于python进阶书,不适合入门读物。另外还有2本进阶书《python cookbook》和《Effective Python:编写高质量Python代码的59个有效方法》。下面是本书的读书笔记,其他2本后面陆续补充。 阅读详情

目录

前言

一、Sentence类第一版:单词序列

Sentence第一版实现

序列可以迭代的原因:iter函数

二、可迭代的对象与迭代器的对比

三、Sentence类第二版:典型的迭代器

第二版Sentence类实现典型的迭代器设计模式

不要把可迭代对象变成自身的迭代器

四、Sentence类第三版:生成器函数

Sentence第三版

生成器函数的工作原理

五、Sentence类第四版:惰性实现

六、Sentence类第五版:生成器表达式

七、何时使用生成器表达式

八、等差数列生成器

ArithmeticProgression类实现等差数列生成器

aritprog_gen生成器函数实现等差数列生成器

使用itertools模块生成等差数列

九、标准库中的生成器函数

第一组:用于过滤的生成器函数

第二组:用于映射的生成器函数

第三组:用于合并的生成器函数

第四组:用于扩展输入的可迭代对象的生成器函数

第五组:用于重新排列元素的生成器函数

十、python3.3出现的新句法:yield from

十一、可迭代的规约函数

十二、深入分析iter函数

十三、略

十四、把生成器当成协程


前言

迭代器模式:扫描内存中放不下的数据集时,要找到一种惰性获取数据项的方式,即按需一次获取一个数据项,这就是迭代器模式。

        python为了抽象出迭代器模式,加入了关键字yield,这个关键字用于构建生成器,其作用与迭代器一样。所有生成器都是迭代器,因为生成器完全实现了迭代器接口。根据《设计模式:可复用面向对象软件的基础》中的定义,迭代器用于从一个集合中取出元素,生成器则用于凭空生成元素,但在python社区中,大多时候把迭代器和生成器视作同一概念。

        在python3中,即使是内置的range()函数也返回一个类似生成器的对象,而在以前都是返回完整的列表,如果一定要range()返回列表,则必须明确指明,list(range(100))。

        python中所有的集合都是可迭代的,python语言内部,迭代器可以支持以下功能:

  • for 循环
  • 构建和扩展集合类型
  • 逐行遍历文本文件
  • 列表推导、字典推导和集合推导
  • 元组拆包
  • 调用函数时,使用*拆包实参

一、Sentence类第一版:单词序列

Sentence第一版实现

import re
import reprlib

RE_WORD = re.compile('\w+') # 匹配字母数字下划线,重复一次或多次

class Sentence:

    def __init__(self, text):
        self.text = text 
        self.words = RE_WORD.findall(text) # 1

    def __getitem__(self, index):
        return self.words[index] # 2

    def __len__(self): # 3
        return len(self.words)

    def __repr__(self): 
        return 'Sentence(%s)' % reprlib.repr(self.text) # 4

s = Sentence('"The time has come,", the Walrus said,')
for word in s:
    print(word)
  1. re.findall函数返回一个字符串列表,里面的元素是正则表达式的全部非重叠匹配
  2. self.words中保存的是findall函数返回的结果,因此直接返回指定索引位上的单词
  3. 为了完善序列协议,实现了__len__方法,但是如果只是为了方对象可迭代,只需要实现__getitem__即可
  4. reprlib.repr用于生成大型数组结构的简略字符串表示,默认情况下最多30个字符

测试结果如下:成功实现迭代

因为实现了__getitem__和__len__,因此这一版Sentence类是序列,可以按索引获取单词:

序列可以迭代的原因:iter函数

解释器需要迭代对象x时,会自动调用iter(x):

  1. 检查对象是否实现了__iter__方法,如果实现了就调用它,获取一个迭代器
  2. 如果没有实现__iter__方法,但是实现了__getitem__方法,python会创建一个迭代器,尝试按顺序从索引0开始获取元素
  3. 如果尝试失败,python抛出TypeError异常,提示 “××× object is not iterable”

任何python序列都可迭代的原因是,他们都实现了__getitem__方法(事实上标准序列也都实现了__iter__方法)。

        在白鹅类型认为:只要实现了__iter__方法(哪怕函数体直接pass,只要有这个函数名就行),那么就认为对象是可迭代的。由于abc.Iterable类实现了__subclasshook__方法,因此不需要创建子类也不需要注册,就可以使用isinstance方法来判断对象是否可迭代,如下:

但是用isinstance()是不能判断第一版的Sentence类的对象的,因为没有实现__iter__方法。要检查对象x是否可迭代,最准确的方法还是调用iter(x),如果不可迭代会抛出TypeError异常,且iter函数会考虑__getitem__方法,而abc.Iterable则不考虑。

二、可迭代的对象与迭代器的对比

python从可迭代的对象中获取迭代器对象。

        可迭代的对象:使用iter内置函数可以获取迭代器的对象。如果对象实现了能返回迭代器的__iter__方法,那么对象是可迭代的。序列都是可迭代的。实现了__getitem__方法,而且其参数是从零开始的索引,这种对象也可以迭代。

        下面是一个简单的for循环,迭代一个字符串,这里字符串'ABC'是可迭代的对象,背后是有迭代器的,只不过我们看不到。

如果不用for语句,用while循环模拟,要像下边这样写:

  1. 用iter函数使用可迭代对象s构建迭代器it
  2. 不断在迭代器上调用next函数,获取下一个字符
  3. 如果没有字符了,迭代器会抛出StopIteration异常
  4. 使用对it的应用,即废弃迭代器对象
  5. 退出循环

标准迭代器接口有两个方法:

  • __next__,返回下一个可用的元素,如果没有元素了,就抛出StopIteration异常。
  • __iter__,返回self,以便在应该使用可迭代对象的时候使用迭代器,比如for循环中。

如下UML图中Iterable和Iterator分别是可迭代的对象与迭代器对象对应的抽象基类。Iterator继承自Iterable。

        检查对象x是否位迭代器最好的方式是调用isinstance(x, abc.Iterator),得益于Iterator.__subclasshook__方法,即使对象x所属的类不是Iterator类的真实子类或虚拟子类,这样检查也是有效的。

        下面例子是从控制台观察迭代器的构建过程,以及用next函数使用迭代器的过程:

  1. 创建一个Sentence实例s3,包含3个单词。
  2. 用iter函数从可迭代对象s3中获取迭代器对象it
  3. 调用next来操作迭代器对象,获取下一个单词
  4. 没有单词了,抛出StopIteration异常
  5. 到头后,迭代器没用了,可以看到用迭代器初始化的列表为空,迭代器已经不能取元素
  6. 如果想再次迭代,要重新构建迭代器

因为迭代器只需__next__和__iter__两个方法,所以出了调用next()方法,以及捕获StopIteration异常之外,没有办法检查是否还有遗留的元素。此外,也没有办法还原迭代器。如果想再次迭代,那就要调用iter函数,传入之前构建迭代器的可迭代对象,再次获取迭代器。

        综上,迭代器定义如下,迭代器是这样的对象:

  • 实现了无参数的__next__方法,返回序列中的下一个元素;
  • 如果没有元素了,就抛出StopIteration异常
  • 实现了__iter__方法,因为只要实现了__iter__方法,那么对象就是可迭代的,因此迭代器也可以迭代,也是可迭代对象

三、Sentence类第二版:典型的迭代器

第二版Sentence类实现典型的迭代器设计模式

import re
import reprlib

RE_WORD = re.compile('\w+')

class Sentence:

    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)

    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)

    def __iter__(self): # 1
        return SentenceIterator(self.words) # 2

class SentenceIterator:

    def __init__(self, words):
        self.words = words # 3
        self.index = 0 # 4

    def __next__(self):
        try:
            word = self.words[self.index] # 5
        except IndexError: 
            raise StopIteration() # 6
        self.index += 1 # 7
        return word # 8

    def __iter__(self): # 9
        return self
  1.  与第一版Sentence类相比,第二版这里多了个__iter__方法,且这一版没有实现__getitem__方法,为的是明确表示这个类可迭代,因为实现了__iter__方法。
  2.  根据可迭代协议,__iter__方法实例化并返回一个迭代器。
  3.  SentenceIterator实例化迭代器对象时需要引用单词列表。
  4.  self.index用于确定下一个要获取的单词,从这里也可以看出,迭代器内部是没有还原机制的,index只加不减。
  5. __next__方法内部获取self.index索引位上的单词。
  6. 如果索引位上已经没有单词,那么抛出StopIterator异常。
  7. 递增self.index的值。
  8. 返回当次迭代到的单词。
  9. 实现self.__iter__方法,返回迭代器对象自身的引用。

在这里示例中,其实没必要在迭代器SentenceIterator类中实现__iter__方法,不过这样做是对的,因为迭代器应该实现__next__和__iter__两个方法,这样做能让迭代器通过issubclass(SentenceInterator, abc,Iterator)测试。如果一开始我们就让SentenceIterator类继承abc.Iterator类,那么它会自动继承abc.Iterator.__iter__这个具体方法。

不要把可迭代对象变成自身的迭代器

  • 可迭代对象:实现__iter__方法,该方法每次都实例化一个新的迭代器。
  • 迭代器:要实现__next__方法,每次返回单个元素,此外还要实现__iter__方法,返回迭代器本身。

        可迭代的对象不是迭代器,是通过生成迭代器来实现迭代。

        除了__iter__方法之外,你可能还想在Sentence类中实现__next__方法,让Sentence实例既是可迭代的对象,也是自身的迭代器,但是这样做非常糟糕,是常见的反模式。在《设计模式》一书中写到了迭代器模式的用途:

  • 访问一个聚合对象的内容而无需暴露它的内部表示
  • 支持对聚合对象的多种遍历
  • 为遍历不同的聚合结构提供一个统一的接口(即支持多态迭代)

        为了"支持多种遍历",必须能从同一个可迭代的对象中获取多个独立的迭代器,而且各个迭代器要能维护自身的内部状态,因此迭代器模式的正确实现方式是:每次调用iter(my_iterable)都新键一个独立的迭代器。

        综上,可迭代的对象一定不能是自身的迭代器,即可迭代的对象必须实现__iter__方法,但不能实现__next__方法。另一方面,迭代器应该一直可以迭代,迭代器的__iter__方法应该返回自身。

四、Sentence类第三版:生成器函数

Sentence第三版

实现相同功能,但却更pythonic的方式是:用生成器函数代替迭代器类SentenceIterator类。

import re
import reprlib

RE_WORD = re.compile('\w+')

class Sentence:

    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)

    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)

    def __iter__(self):
        for word in self.words: # 1
            yield word # 2
        return # 3
  1. for循环迭代self.words
  2. 产出当前的word
  3. 这个return不是必要的,这个函数可以直接落空,自动返回。不管有没有return语句,生成器函数都不会抛出StopIteration异常,而是在生成完全部值之后会直接退出。

用以上方法,不需要再定义一个迭代器类!

        在第二版的的Sentence类中,可迭代对象的__iter__方法会调用SentenceIterator类的构造函数创建一个迭代器并将其返回,而在第三版中,则是每次调用__iter__方法都会自动创建一个生成器,这里的__iter__方法实际上是一个生成器函数。

生成器函数的工作原理

只要python函数的定义体中有yield关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象,即生成器函数是生成器工厂。

        下面是一个生成器函数示例:

  1. 该函数是生成器函数,因为函数体中包含关键字yield。
  2. 生成器函数定义体中通常有循环,但这不是必要条件,示例函数中定义体是三个yield语句。
  3. gen_123是一个函数对象,python中的函数都是一等对象。
  4. 但是调用gen_123()时,返回一个生成器对象。
  5. 生成器是迭代器,会生成传给yield关键字的表达式的值,这里虽然没有循环,但是有三个yield语句可以生成三个值,即1 2 3。
  6. 把生成器对象赋值给g
  7. 因为g是迭代器,因此调用next(g)可以获取yield生成的下一个元素。
  8. 生成器函数的定义体执行完毕后,生成器对象会抛出StopIteration异常。

生成器函数的内部工作机制为:

  1. 调用生成器函数会创建一个生成器对象,包裹生成器函数的定义体。
  2. 把生成器函数传给next()函数时,生成器函数会向前,执行函数定义体中的写一个yield语句,返回产出的值,并在函数定义体的当前位置暂停。
  3. 最终函数定义体执行完毕返回时,外层的生成器对象会抛出StopIteration异常。

下面例子使用for循环更清楚地说明生成器函数体的执行过程:

  1. 定义生成器函数的方式与普通函数一样,只不过要使用yield关键字。
  2. 在标号5处,for循环第一次隐式调用next函数时,会打印start,然后停在第一个yield语句处,生成值 ‘A’
  3. 在for循环第二次隐式调用next函数时,会打印'continue',然后停在第二个yield语句,生产值'B'。
  4. 第三笔调用next()函数时,会打印end,然后到达函数定义体的末尾,导致生成器对象抛出StopIterator异常。
  5. 迭代时,for机制的作用与 g = iter(gen_AB())一样,用于获取生成器对象(),然后每次迭代时调用next(g),即生成器对象只有一份,而next(g)要调用多次。
  6. 循环体打印 ---> 和next(g)返回的值,但是生成器函数体中的print函数输出结果之后才会看到这个输出。
  7. start是生成器函数体中print语句输出的结果。
  8. 生成器函数体中的yield ‘A’ 语句会生成值A,提供给for循环使用,而A会赋值给变量C,最终输出 ---> A,然后生成器停在这一句。
  9. for循环隐式第二次调用next(g), 生成器从上边生成A的那一句继续执行,于是再次执行到生成器函数定义体中的print语句。
  10. yield 'B' 生成值B,提供给for循环使用,生成器停在这一句。
  11. 第三次调用next(g),执行print语句,继续迭代,前进到了生成器函数定义体的末尾,生成器对象抛出StopIterator异常。for机制会捕获异常,因此循环到此终止没有报错。

五、Sentence类第四版:惰性实现

惰性:比如Iterator的接口,next(my_iterator)一次生成一个元素。前三版Sentence类设计都不具有惰性,因为__init__方法急迫地都见好了文本中的单词列表self.words,这样就得处理整个文本,非常占内存。

        re.finditer函数是re.findall函数的惰性版本,返回的不是列表,而是一个生成器,按需生成re.MatchObject实例。如有有很多匹配,re.finditer函数能节省大量内存。

  1.  不再需要words列表。
  2.  finditer函数构建一个生成器,包含self.text中匹配RE_WORD的单词,产出MatchObject实例match。
  3. match.group()方法从MatchObject实例中提取匹配正则表达式的具体文本。

六、Sentence类第五版:生成器表达式

简单的生成器函数可以替换成生成器表达式。

        生成器表达式可以理解为列表推导式的惰性版本:不会迫切地构建列表,而是返回一个生成器,按需惰性生成元素。也就是说,列表推导是制造列表的工厂,生成器表达式是制造生成器的工厂。

 

  1.  gen_AB是生成器函数。
  2.  列表推导迫切地迭代生成器函数gen_AB产生的生成器对象,来得到列表res1。
  3. 用for循环来迭代res1列表,所有序列都是可迭代对象。
  4. 把生成器表达式生成的生成器赋值给res2。这里生成器函数的函数体并没有真正执行,只有当需要取元素的时候才会执行。
  5. res2是一个生成器对象。
  6. 只有for循环迭代生成器res2时,gen_AB才会真正执行,for循环每个迭代时会隐式调用next(res2),前进到gen_AB函数中的下一个yield语句。

与上一个版本的区别是__iter__方法,这里__iter__不再是生成器函数了,没有用yield,而是使用生成器表达式构建的生成器,然后将其返回,最终效果一样:调用__iter__方法得到一个生成器对象。

        生成器表达式是语法糖:完全可以替换成生成器函数,只不过有时候用生成器表达式更方便。

七、何时使用生成器表达式

  • 如果生成器表达式要分成多行写,那么请使用生成器函数写。
  • 此外,生成器函数可以使用多个语句实现更复杂的逻辑,也可以作为协程使用。
  • 且生成器函数有名称,因此可以重用。

八、等差数列生成器

典型的迭代器模式作用很简单,就是遍历数据结构。

        但即使不是从集合中获取元素,而是 获取序列中即时生成的下一个值时,也用得到这种基于方法的标准接口。例如,内置的range函数用于生成有穷整数等差数列,itertools.count函数用于生成无穷等差数列。

ArithmeticProgression类实现等差数列生成器

  1. __init__方法有两个参数,begin和step,end是可选的,如果end的值为None,那么生成无穷序列。
  2. 把self.begin赋值给result,赋值之前先强制类型转换,转换成前边self.being + self.step得到的类型(算术运算符会隐式进行强制类型转换),这是为了保证数列的首项与其它项的类型一样。
  3. 为了提高可读性,创建forever变量,如果self.end的值是None,那么forever的值是True,生成的是无穷序列。
  4. forever等于True即self.end等于None的时候,会一直循环下去,生成无穷序列,否则当result大于等于self.end的时候结束。循环退出则函数也随之退出。
  5. 生成当前的result值。
  6. 生成可能存在的下一个结果,并重新绑定到result上。这个值可能永远不会产出,因为while循环可能会终止。

ArithmeticProgression类的用法如下:

aritprog_gen生成器函数实现等差数列生成器

如果只是为了得到一个等差数列生成器,其实没必要定义一个类,直接定义一个生成器函数也成,生成器函数也可以有参数,如下所示:

使用itertools模块生成等差数列

python3.4的itertools模块提供了19个生成器函数。例如,itertools.count函数返回的生成器能生成多个数。如果不传入参数,itertools.count函数会生成从零开始的整数数列,也可以提供start和step值,可以生成等差数列生成器,如下所示: 

但是itertools.count函数是不能指定end的,即不能停止,如果调用list(count),python会创建一个特别大的列表,超出可用内存。

        itertools.takewhile函数可以生成一个使用另一个生成器的生成器,在指定的条件计算结果为False时停止。可以把takewhile和count函数一起 使用,如下,takewhile第二个参数是一个生成器,其生成的内容传入第一个参数(同时也是takewhile返回的生成器的生成值),第一个参数是一个返回布尔值的函数,当其返回值为False时,则停止产出元素。

 下边例子是使用takewhile和count实现的等差数列生成器函数:

该例中arirprog不是生成器函数,因为定义体中没有yield关键字,但是它会返回一个生成器,因此它与生成器函数一样,都是生成器工厂函数。

九、标准库中的生成器函数

python标准库提供了很多生成器,有用于逐行迭代纯文本文件的对象,还有出色的os.walk函数。这个函数在遍历目录树的过程中产出文件名,因此递归搜索文件系统像for循环那样简单。

        本节关注通用的生成器函数:参数为任意的可迭代对象,返回值是生成器,用于生成选中的、计算出的和重新排列的元素。按函数的高阶功能进行分组介绍。

第一组:用于过滤的生成器函数

从输入的可迭代对象中产出元素的子集,而不修改元素本身。如同itertools.takewhile函数一样,大多数用于过滤的生成器函数都接受一个断言参数,该参数是个布尔函数,有一个参数,会应用到输入中的每个元素上,用于判断是否将元素包含在输出中。

下面是控制台中演示各个用于过滤的生成器函数的用法:

注意dropwhile是一直跳过满足条件的元素,然后当遇到第一个不满足条件的元素以后就不再检查以后的元素,而是直接产出,因为上边例子中跳过了Aa,当遇到r时不满足条件了,因此从r开始全部产出。

第二组:用于映射的生成器函数

如果输入来自多个可迭代的对象,第一个可迭代的对象到头后就停止输出。

  1. 计算总和
  2. 计算最小值
  3. 计算最大值
  4. 计算乘积
  5. 从1!到10!,计算各个数的阶乘

注意在生成器函数accumulate产生的生成器中,第一个元素是直接产出的,因为要保证元素数目与原来的可迭代对象一致吧。

        下面演示用于映射的其他生成器函数

  1.  enumerate从1开始,为字符串中每个字母编号,编号与字母组成一个元组。
  2.  从0到10,计算各个整数的平方。
  3.  计算两个可迭代对象中对应位置上的两个元素之积,元素最少的那个可迭代对象到头后就停止。
  4.  作用等同于内置的zip函数,将两个可迭代对象中的元素打包。
  5.  把enumerate产出的可迭代对象用*拆包作为参数传给mul,然后产生新的元素,这里是将字母重复其对应的索引次。
  6.  accumulate(sample)计算累加和,然后用enumerate从索引1开始超出索引元素对,再进行拆包送入到匿名函数中。

第三组:用于合并的生成器函数

用于合并多个可迭代对象。

下面例子演示用于合并的生成器函数:

  1.  chain没什么意思,参数是多个可迭代对象,chain函数返回的生成器就是把这些可迭代对象中的元素串起来生成。
  2.  如果只传入一个可迭代对象,那么chain函数没啥用。
  3.  enumerate()函数返回的生成器生成的对象是元素和索引组成的组合,该组合也是可迭代对象(就是元组),chain.from_iterable把元组也拆开进行产出。
  4. zip把两个可迭代对象合并成一系列由两个元素组成的元组。
  5. zip可以处理任意数量个可迭代对象,但是只要有一个可迭代对象到头了,生成器就停止。
  6. itertools.zip_longest函数的作用与zip类似,不过输入的所有可迭代对象都可以处理到头,如果有些提前用完则会填充None。
  7. fillvalue关键字用于指定填充的值。

itertools.product生成器函数是计算笛卡尔积的惰性方式:

  1.  三个字符的字符串和两个整数的值域得到的笛卡尔积是六个元组,因为3 * 2 = 6。
  2.  两张牌(‘AK’)与四种花色得到的笛卡尔积是八个元组。
  3.  如果只传入一个可迭代对象,product函数产出的是一系列只有一个元素的元组,没啥用。
  4. repeat=N关键字参数告诉product函数重复N次处理输入的各个可迭代对象。

第四组:用于扩展输入的可迭代对象的生成器函数

itertools模块中的count和repeat函数返回的生成器可以无中生有:即不接受可迭代对象作为参数。cycle生成器会备份输入的可迭代对象,然后重复产出对象中的元素。

  1.  使用count函数构建生成器ct,没有给count函数传入参数。
  2.  因为没有给count函数传入参数,因此默认从0开始产出。
  3.  使用生成器ct依次产出元素。
  4.  用islice对count作了限制,这个时候可以构建列表,但是不能直接用ct构建列表,因为ct是无穷的。
  5.  使用‘ABC’构建一个生成器cy,然后获取其第一个元素。
  6.  这里也是使用islice进行了限制,才能构建列表,否则cy也是无穷的。
  7.  构建一个repeat生成器,始终产出数字7。
  8.  传入times参数可以限制repeat生成器生成的元素数量,这里生成4次数字8。
  9.  repeat函数的常见用途:为map函数提供固定参数,这里提供的是乘数5。

 combinations、comb、permutations生成器函数,连同product函数,称为组合学生成器。

  1. combinations将‘ABC’中的元素每两个凑成一个元组,生成元组中元素的顺序无关紧要。但是生成元组中没有相同元素。
  2. combinations_with_replacement也将‘ABC’中每两个元素凑成一个元组,但是允许元组包含相同的元素。
  3. permutations将‘ABC’中每两个元素构成一个元组,且元素在元组中的顺序很重要。
  4. 用product得到‘ABC’与‘ABC’的笛卡尔积。

第五组:用于重新排列元素的生成器函数

  1.  groupby函数第一个参数是生成器,第二个参数是分类标准,且产出(key, group_generator)这种形式的元组,其中group_generator产生的都是key这个类别的元素。因为这里没有指定分类函数(即分类标准),因此使用相同的元素作为分类标准。注意这里所有相同的元素都在一起,因为itertools.groupby假定输入的可迭代对象要使用分组的标准排序。
  2.  用char、group将groupby产生的元组拆包,char即key,group即group_generator。
  3.  为了使用groupby函数,要排序输入,这里按照单词的长度排序。
  4.  用length、group拆包生成器生成的元素
  5.  使用reverse生成器从右向左迭代animals。

itertools.tee作用是产生多个生成器,都能生成传入的可迭代对象参数中的元素,默认情况下是产生两个生成器。

十、python3.3出现的新句法:yield from

在chain生成器函数中,it是外层循环迭代到的生成器,内存循环迭代it,用yield i 来产生it中的元素。用yield from 句法可以取代内层循环:

使用yield from 后加上可迭代对象,可以把可迭代对象中的元素一个个yield出来,对比yield来说代码更简洁,不用使用循环来yield元素。

        但yield from并不只是一个语法糖,yield from 还会创建通道,把内层生成器直接与外层生成器的客户端联系起来。把生成器当成协程使用时,这个通道特别重要,不仅能为客户端代码生成值,还能使用客户端代码提供的值。

十一、可迭代的规约函数

规约函数都接受一个可迭代的对象,然后返回单个结果。下边列出的每个内置函数都可以使用functools.reduce函数实现,内置是因为它们便于解决常见的问题。此外,对all和any函数来说,相比reduce函数有优化,即这两个函数会短路,一旦确定结果就立即停止使用迭代器。

sorted:虽然sorted不是生成器函数,但是因为太重要还是放在这里对比。内置函数sorted可以接受任意的可迭代对象(当然,必须是最终会停止的可迭代对象,否则函数会一直收集元素永远无法返回结果),sorted会构建并返回真正的列表,毕竟要读取可迭代对象中的每一个元素才能够排序,而且排序的对象是列表。

十二、深入分析iter函数

在python中迭代对象x时会调用iter(x)。

        iter函数还有一个鲜为人知的用法:传入两个参数,使用普通函数或任何可调用对象创建迭代器。第一个参数必须是可调用对象,用于不断调用(没有参数),产出各个值;第二个值是哨符,即标记值,当第一个参数,即可调用对象返回这个值时,触发迭代器抛出StopIteration异常,而不产出哨符。

十三、略

十四、把生成器当成协程

略。

    

Ubuntu docker镜像中下载安装 DPABI(DPARSF) 在Ubuntu服务器的docker镜像中用MATLAB运行DPABI(以及使用DPARSF的功能)。 阅读详情

相关推荐

MapGIS地质剖面图绘制实战:从数据准备到整饰出图

本文详细解析了MapGIS地质剖面图绘制的全流程,从数据准备到整饰出图,涵盖了等高线处理、GRD格网化、比例尺控制等关键步骤。通过实战技巧和案例分析,帮助地质工作者高效完成专业地质剖面图绘制,提升矿产勘探和工程勘察的工作效率。

weixin_28335283的博客 591

DL4J中文文档/语言处理/SentenceIterator

句子迭代器 SentenceIterator (句子迭代器)用于 Word2vec 和 词袋 。 它将一些文本以向量的形式输入到神经网络中,也涵盖了文本处理中的文档概念。 在自然语言处理中,文档或句子通常用来封装算法应该学习的上下文。 一些例子包括分析推文和成熟的新闻文章。句子迭代器的目的是把文本分成可处理的位。注意句子迭代器是输入不可知的。因此,一些文本(文档)可以来自文件系统、Twit...

bewithme的专栏 617

部署资源管理系统chemx

资源管理系统的部署教程

weixin_40784215的博客 822

python创建等差数列

import numpy as np x=int(input("请输入等差数列最小值=")) y=int(input("请输入等差数列最大值y=")) z=int(input("请输入等差数列长度z=")) a = np.linspace(x,y,z) print (a) 运行之后分别输入等差数列的最小值,最大值,长度(有多少个数字): 例题:创建一个范围在0~100之间,长度为11的等差数列: 运行代码: 回车输出结果: ...

qq_53551567的博客 1万+

【前端HTML生成条形码——MQ】

本文将展示前端HTML如何根据用户输入内容生成对应的条形码,附HTML和JS代码。代码如下(示例):

weixin_42588676的博客 1955

【深大计算机网络】实验三 可靠传输机制 实验报告 附代码

【内附代码】【深大计算机网络】实验三 可靠传输机制 实验报告 附代码

m0_74268508的博客 1653

python迭代器和生成器

帮助大家深入了解迭代器与生成器

qq_52758588的博客 1万+

嵌入式状态机编程-QP状态机框架与常见状态机方法

状态机基本术语 现态:是指当前所处的状态。 条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。 传统有限状态机Fsm实现方法 如图,是一个定时计数器,计数器存在两种状态,一种为设置状态,一种为计时状态 设

qq_36969440的博客 9661

Python:魔术方法(__getitem__、__len__等包含双下划线构成的方法)的简介、使用案例之详细攻略

Python:魔术方法(__getitem__、__len__等包含双下划线构成的方法)的简介、使用案例之详细攻略目录魔术方法Magic Method的简介(1)、举例理解Magic Method常用的魔术方法使用案例1、常用方法(1)、__init__:初始化方法(2)、__len__():返回容器中元素的个数2、比较操作符(1)、__lt__(self, other):定义小于号的行为3、算数运算符(1)、__add__(self, other):定义加法的行为:+4、增量赋值运算(1)、__iadd_

头部AI社区如有邀博主AI主题演讲请私信—心比天高,仗剑走天涯,保持热爱,奔赴向梦想!低调,专注,谦虚,自律,反思,成长,还算比较正能量的博主,公益免费传播…内心特别想在AI界做出一些可以推进历史进程影响力的技术(兴趣使然,有点小情怀,也有点使命感呀 3079

RAP 通用搜索实战:用 CDS 注解打造 Fiori Elements 的一体化检索体验

RAP应用中的通用搜索优化实践:通过CDS注解实现高效模糊匹配 摘要:本文介绍了在SAP RAP应用中优化列表搜索体验的方法。通过CDS注解配置通用搜索功能,用户无需指定字段即可在多字段中模糊匹配数据。关键实现包括:1)使用@Search.searchable启用全局搜索;2)通过@Search.defaultSearchElement标记核心搜索字段;3)利用@Search.fuzzinessThreshold设置容错阈值(0.7-1.0)处理拼写错误;4)使用@Search.ranking优化结果排序。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1397

YOLOv12改进策略【独家融合改进】| MobileNetV4+BiFPN,轻松实现降参涨点,适用专栏内所有的骨干替换

可扩展的高效物体检测BiFPN(加权双向特征金字塔网络)

Limiiiing的博客 978

Python.__getitem__方法

Python的魔法方法__getitem__ 可以让对象实现迭代功能,这样就可以使用for…in… 来迭代该对象了 class Animal: def __init__(self, animal_list): self.animals_name = animal_list animals = Animal(["dog","cat","fish"]) for animal ...

Suixing_yuan的专栏 3036

使用 MATLAB/Simulink 构建一个电机-液压联合控制系统的仿真模型

本文详细介绍了基于Simulink的电机与液压执行机构联合控制系统建模方法。通过7个步骤,系统讲解了从创建项目、搭建PMSM电机模型、建立逆变器与矢量控制模块,到构建液压泵和执行机构模型的完整过程。重点展示了如何设置关键参数、添加测量反馈模块,并提供了实用的代码片段。教程还包含仿真运行流程、结果分析指标及进阶建议,帮助读者掌握多物理场耦合系统建模的核心技术。该方法适用于机电液一体化系统设计,为工程实践提供有效参考。

amy_mhd的博客 613

完全理解python迭代对象_流畅的python读书笔记-第十四章-可迭代的对象、迭代器和生成器...

可迭代的对象、迭代器和生成器理念迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式(Iterator pattern)。看个例子import reimport reprlibRE_WORD = re.compile('\w+')class Sentence:def __init__(self, text):self.te...

weixin_39600823的博客 134

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

流畅的python读书笔记-第十四章-可迭代的对象、迭代器和生成器

可迭代的对象、迭代器和生成器 理念 迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式(Iterator pattern)。 看个例子 import re import reprlib RE_WORD = re.compile('\w+') class S...

weixin_33767813的博客 158

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

《流畅的python》学习笔记之可迭代的对象,迭代器和生成器

前言 可迭代对象,迭代器,生成器,不知道大家第一眼看到这几个名词的时候,有没有一种很熟悉的感觉,然后在脑子里飞速找寻这几个词之间的差异,但是找了半天,似乎没有找到,并且让原本清晰的概念变得模糊了。 反正我一开始有这种感觉,不过现在看完这一章的内容,好像还是有点晕哈哈哈,不过还是乘热打铁,在复习巩固一边,应该会清晰一点! 首先我们来看一下各自的定义,这里我会参考书上的内容然后结合自己的理解来定义这三者。 可迭代对象 首先我们可以问这样一个问题:什么是可迭代对象?这个可能比较抽象不好理

max_LLL的博客 466

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

流畅的python 14章可迭代的对象、迭代器 和生成器

可迭代的对象、迭代器和生成器 迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式(Iterator pattern)。 迭代器用于从集合中取出元素;而生成器用于“凭空”生成元素。通过斐波纳契数列能很好地说明二者之间的区别:斐波纳契数列中的数有无穷个,在一个集合里放不下。不过要知道,在 Python社区...

dianshuochi1092的博客 236

流畅的Python(十四)-可迭代对象、迭代器和生成器

1. 使用iter内置函数处理可迭代对象的方式2. 如何使用Python实现经典的迭代器模式3. 生成器函数工作原理4. 使用生成器函数或生成器表达式代替经典的迭代器5. 使用yield from语句合并生成器。

weixin_37901366的博客 753

《流畅的Python》学习笔记(11):可迭代的对象、迭代器和生成器

在Python中,使用频次最高,最实用的一个特性就是迭代的特性,尤其是在for循环,读写文本中大有用处,这次我们来仔细分析Python中的迭代。

baidu_34912627的博客 248

流畅的Python: 可迭代的对象、迭代器和生成器

文章目录1. 单词序列Sentence2. 序列可以迭代的原因:iter函数3. 可迭代的对象与迭代器的对比 1. 单词序列Sentence import re import reprlib RE_WORD = re.compile("\w+") class Sentence: ''' 定义了一个 Sentence 类,通过索引从文本中提取单词。 ''' d...

随心写写 229

Python入门笔记8 - 可迭代对象、迭代器、生成器

介绍python中可迭代对象、生成器、迭代器的相关概念

佚失的诗篇 276

猴子都能看懂的迭代器、生成器、可迭代对象解析(python)

通过官方文档让你看的懂看的对,真正了解迭代器、生成器、可迭代对象迭代器、生成器、可迭代对象、迭代器协议Demo 实现python3.6中的range方法改进:使用生成器代替上述代码中的迭代器类总结 迭代器、生成器、可迭代对象、迭代器协议 先说好这篇文章是根据python3.6 写出来的,其他语言博主不清楚是否和Python一样 迭代器协议在Python3.6的文档(4.5. Iterator T...

baidu_36146918的博客 262

流畅的Python: 可迭代的对象、迭代器和生成器2

文章目录1. 典型的迭代器2. 生成器函数3. Sentence类升级版:惰性实现4. Sentence类终极版:生成器表达式 1. 典型的迭代器 import re import reprlib RE_WORD = re.compile("\w+") class SentenceIterator: def __init__(self, words): self.wo...

随心写写 177

python 可迭代对象 迭代器 生成器 生成器表达式 iter()

本文为阅读《流畅的python》14章 总结 可迭代对象 迭代器 生成器 生成器表达式 yield from iter() 可迭代对象 可以被内置函数iter处理的对象 内置iter函数会检查对象是否实现了__iter__方法,如果实现了是就调用它,获取一个迭代器 如果没有实现__iter__方法,但是实现了__getitem__方法,python会创建一个迭代器,尝试从索引0开始获取元素 如果以...

无境 337

迭代器与生成器

文章目录前言可迭代对象可迭代对象与迭代器的对比生成器函数标准库中的生成器函数深入分析iter函数 前言 文章是《流畅的Python》学习笔记与个人总结。 可迭代对象 序列可以迭代的原因:iter函数 iter函数的作用: 检查对象是否实现了 iter 方法,如果实现了就调用它,获取一个迭代器。 如果没有实现 iter 方法,但是实现了 getitem 方法, Python 会创建一个迭代器,尝试按顺序(从索引 0 开始)获取元素。 如果尝试失败, Python 抛出 TypeError 异常,通常会提示“

奔跑的蜗牛的博客 406

python中生成器和迭代器的理解

该理解是从《流畅的python》一书中进行学习和总结 序列可以迭代的原因:iter函数 解释器需要迭代对象 x 时,会自动调用 iter(x)。 内置的 iter 函数有以下作用。 (1) 检查对象是否实现了 iter 方法,如果实现了就调用它,获取一个迭代器。 (2) 如果没有实现 iter 方法,但是实现了 getitem 方法,Python 会创建一个迭 代器,尝试按顺序(从索引 0 开始)获取元素。 (3) 如果尝试失败,Python 抛出 TypeError 异常, 其中迭代器分为可迭代的对象和迭

坚持的力量 559
上一篇: 流畅的python笔记(十三)正确重载运算符
下一篇: 流畅的python笔记(十五)上下文管理器和else块
罗古洞的女婿
博客等级 码龄10年 1117粉丝 125原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值