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

目录

前言

一、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异常,而不产出哨符。

十三、略

十四、把生成器当成协程

略。

    

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值