zoukankan      html  css  js  c++  java
  • 流畅的python 14章可迭代的对象、迭代器 和生成器

    可迭代的对象、迭代器和生成器

    迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式(Iterator pattern)。

    迭代器用于从集合中取出元素;而生成器用于“凭空”生成元素。通过斐波纳契数列能很好地说明二者之间的区别:斐波纳契数
    列中的数有无穷个,在一个集合里放不下。不过要知道,在 Python社区中,大多数时候都把迭代器和生成器视作同一概念。


    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 __getitem__(self, index):
            return self.words[index] ➋
        def __len__(self): ➌
            return len(self.words)
        def __repr__(self):
            return 'Sentence(%s)' % reprlib.repr(self.text) ➍

    ❶re.findall 函数返回一个字符串列表,里面的元素是正则表达式的
    全部非重叠匹配。
    ❷ self.words 中保存的是 .findall 函数返回的结果,因此直接返回
    指定索引位上的单词。
    ❸ 为了完善序列协议,我们实现了 __len__ 方法;不过,为了让对象
    可以迭代,没必要实现这个方法。
    ❹ reprlib.repr 这个实用函数用于生成大型数据结构的简略字符串表
    示形式。

    示例 14-2 测试 Sentence 实例能否迭代

    >>> s = Sentence('"The time has come," the Walrus said,') #
    >>> s
    Sentence('"The time ha... Walrus said,') #
    >>> for word in s: #
    ... print(word)
    The
    time
    has
    come
    the
    Walrus
    said
    >>> list(s) #
    ['The', 'time', 'has', 'come', 'the', 'Walrus', 'said']

    ❶ 传入一个字符串,创建一个 Sentence 实例。
    ❷ 注意,__repr__ 方法的输出中包含 reprlib.repr 方法生成的
    ...。
    ❸ Sentence 实例可以迭代,稍后说明原因。
    ❹ 因为可以迭代,所以 Sentence 对象可以用于构建列表和其他可迭代
    的类型。

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

    解释器需要迭代对象 x 时,会自动调用 iter(x)。内置的 iter 函数有以下作用。

    • (1) 检查对象是否实现了 __iter__ 方法,如果实现了就调用它,获取一个迭代器。
    • (2) 如果没有实现 __iter__ 方法,但是实现了 __getitem__ 方法,Python 会创建一个迭代器,尝试按顺序(从索引 0 开始)获取元素。
    • (3) 如果尝试失败,Python 抛出 TypeError 异常,通常会提示“C objectis not iterable”(C 对象不可迭代),其中 C 是目标对象所属的类。任何 Python 序列都可迭代的原因是,它们都实现了 __getitem__ 方法。其实,标准的序列也都实现了 __iter__ 方法,因此你也应该这么做。之所以对 __getitem__ 方法做特殊处理,是为了向后兼容,而未来可能不会再这么做

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

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


    我们要明确可迭代的对象和迭代器之间的关系:Python 从可迭代的对象中获取迭代器。

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

    >>> s = 'ABC'
    >>> for char in s:
    ... print(char)
    ...
    A
    B
    C
    ###如果没有 for 语句,不得不使用 while 循环模拟,要像下面这样写:
    >>> s = 'ABC'
    >>> it = iter(s) #
    >>> while True:
    ... try:
    ... print(next(it)) #
    ... except StopIteration: #
    ... del it #
    ... break #
    ...
    A
    B
    C

    ❶ 使用可迭代的对象构建迭代器 it。
    ❷ 不断在迭代器上调用 next 函数,获取下一个字符。
    ❸ 如果没有字符了,迭代器会抛出 StopIteration 异常。
    ❹ 释放对 it 的引用,即废弃迭代器对象。
    ❺ 退出循环。

    StopIteration 异常表明迭代器到头了。Python 语言内部会处理 for循环和其他迭代上下文(如列表推导、元组拆包,等等)中的StopIteration 异常。


    标准的迭代器接口有两个方法。
    __next__
      返回下一个可用的元素,如果没有元素了,抛出 StopIteration异常。
    __iter__
      返回 self,以便在应该使用可迭代对象的地方使用迭代器,例如在 for 循环中。

    迭代器

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

    典型的迭代器

    使用迭代器模式实现 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): #与前一版相比,这里只多了一个 __iter__ 方法。这一版没有
                               #__getitem__ 方法,为的是明确表明这个类可以迭代,因为实现了
                            #__iter__ 方法。
            return SentenceIterator(self.words) #根据可迭代协议,__iter__ 方法实例化并返回一个迭代器。
    class SentenceIterator:
        def __init__(self, words):
            self.words = words #SentenceIterator 实例引用单词列表。
            self.index = 0#self.index 用于确定下一个要获取的单词。
        def __next__(self):
            try:
                word = self.words[self.index] #获取 self.index 索引位上的单词。
            except IndexError:
                raise StopIteration() #如果 self.index 索引位上没有单词,那么抛出 StopIteration 异
    常。
            self.index += 1 
            return word 
        def __iter__(self): #实现 self.__iter__ 方法。
            return self            

    生成器函数

    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: 
                yield word 
        return

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

    惰性实现

    目前实现的几版 Sentence 类都不具有惰性,因为 __init__ 方法急迫地构建好了文本中的单词列表,然后将其绑定到 self.words 属性上。
    这样就得处理整个文本,列表使用的内存量可能与文本本身一样多(或许更多,这取决于文本中有多少非单词字符)。如果只需迭代前几个单词,大多数工作都是白费力气。
    只要使用的是 Python 3,思索着做某件事有没有懒惰的方式,答案通常都是肯定的。

    re.finditer 函数是 re.findall 函数的惰性版本,返回的不是列表,而是一个生成器,按需生成 re.MatchObject 实例。如果有很多匹配,re.finditer 函数能节省大量内存。我们要使用这个函数让第 4版 Sentence 类变得懒惰,即只在需要时才生成下一个单词。代码如示
    例 14-7 所示。示例 14-7 sentence_gen2.py: 在生成器函数中调用 re.finditer生成器函数,实现 Sentence 类

    import re
    import reprlib
    RE_WORD = re.compile('w+')
    class Sentence:
        def __init__(self, text):
            self.text = text 
        def __repr__(self):
            return 'Sentence(%s)' % reprlib.repr(self.text)
        def __iter__(self):
            for match in RE_WORD.finditer(self.text): 
            yield match.group() 

    ❶ 不再需要 words 列表。
    ❷ finditer 函数构建一个迭代器,包含 self.text 中匹配 RE_WORD
    的单词,产出 MatchObject 实例。
    ❸ match.group() 方法从 MatchObject 实例中提取匹配正则表达式的
    具体文本。
    生成器函数已经极大地简化了代码,但是使用生成器表达式甚至能把代
    码变得更简短。

    生成器表达式

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

    >>> def gen_AB(): #
    ... print('start')
    ... yield 'A'
    ... print('continue')
    ... yield 'B'
    ... print('end.')
    ...
    >>> res1 = [x*3 for x in gen_AB()] #
    start
    continue
    end.
    >>> for i in res1: #
    ... print('-->', i)
    ...
    --> AAA
    --> BBB
    >>> res2 = (x*3 for x in gen_AB()) #
    >>> res2 #
    <generator object <genexpr> at 0x10063c240>
    >>> for i in res2: #
    ... print('-->', i)
    ...
    start
    --> AAA
    continue
    --> BBB
    end.

    ❶ gen_AB 函数与示例 14-6 中的一样。
    ❷ 列表推导迫切地迭代 gen_AB() 函数生成的生成器对象产出的元
    素:'A' 和 'B'。注意,下面的输出是 start、continue 和 end.。
    ❸ 这个 for 循环迭代列表推导生成的 res1 列表。
    ❹ 把生成器表达式返回的值赋值给 res2。只需调用 gen_AB() 函数,
    虽然调用时会返回一个生成器,但是这里并不使用。
    ❺ res2 是一个生成器对象。
    ❻ 只有 for 循环迭代 res2 时,gen_AB 函数的定义体才会真正执
    行。for 循环每次迭代时会隐式调用 next(res2),前进到 gen_AB 函
    数中的下一个 yield 语句。注意,gen_AB 函数的输出与 for 循环中
    print 函数的输出夹杂在一起。

    使用生成器表达式实现 Sentence类

    import re
    import reprlib
    RE_WORD = re.compile('w+')
    class Sentence:
        def __init__(self, text):
            self.text = text
        def __repr__(self):
            return 'Sentence(%s)' % reprlib.repr(self.text)
        def __iter__(self):
            return (match.group() for match in RE_WORD.finditer(self.text))

    这里不是生成器函数了(没有 yield),而是使用生成器表达式构建生成器,然后将其返回。不过,最终的效果一样:调用 __iter__ 方法会得到一个生成器对象。
    生成器表达式是语法糖:完全可以替换成生成器函数,不过有时使用生成器表达式更便利。

    标准库中的生成器函数

    用于过滤的生成器函数

    模块 函数 说明
    itertools

    compress(it,
    selector_it)

    并行处理两个可迭代的对象;如果 selector_it
    中的元素是真值,产出 it 中对应的元素

    itertools

    dropwhile(predicate,
    it)

    处理 it ,跳过 predicate 的计算结果为真值的元
    素,然后产出剩下的各个元素(不再进一步检
    查)

    内置 filter(predicate, it)

    把 it 中的各个元素传给 predicate ,如果
    predicate(item) 返回真值,那么产出对应的元
    素;如果 predicate 是 None ,那么只产出真值元

    itertools

    filterfalse(predicate,
    it)

    与 filter 函数的作用类似,不过 predicate 的
    逻辑是相反的: predicate 返回假值时产出对应
    的元素

    itertools

    islice(it, stop) 或
    islice(it, start,
    stop, step=1)

    产出 it 的切片,作用类似于 s[:stop] 或
    s[start:stop:step] ,不过 it 可以是任何可迭代
    的对象,而且这个函数实现的是惰性操作

    itertools

    takewhile(predicate,
    it)

    predicate 返回真值时产出对应的元素,然后立
    即停止,不再继续检查

    >>> def vowel(c):
    ... return c.lower() in 'aeiou'
    ...
    >>> list(filter(vowel, 'Aardvark'))
    ['A', 'a', 'a']
    >>> import itertools
    >>> list(itertools.filterfalse(vowel, 'Aardvark'))
    ['r', 'd', 'v', 'r', 'k']
    >>> list(itertools.dropwhile(vowel, 'Aardvark'))
    ['r', 'd', 'v', 'a', 'r', 'k']
    >>> list(itertools.takewhile(vowel, 'Aardvark'))
    ['A', 'a']
    >>> list(itertools.compress('Aardvark', (1,0,1,1,0,1)))
    ['A', 'r', 'd', 'a']
    >>> list(itertools.islice('Aardvark', 4))
    ['A', 'a', 'r', 'd']
    >>> list(itertools.islice('Aardvark', 4, 7))
    ['v', 'a', 'r']
    >>> list(itertools.islice('Aardvark', 1, 7, 2))
    ['a', 'd', 'a']

    用于映射的生成器函数

    在输入的单个可迭代对象(map 和starmap 函数处理多个可迭代的对象)中的各个元素上做计算,然后返回结果

    模块 函数 说明
    itertools

    accumulate(it,
    [func])

    产出累积的总和;如果提供了 func ,那么把前两个
    元素传给它,然后把计算结果和下一个元素传给
    它,以此类推,最后产出结果

    内置

    enumerate(iterable,
    start=0)

    产出由两个元素组成的元组,结构是 (index,
    item) ,其中 index 从 start 开始计数, item 则从
    iterable 中获取

    内置

    map(func, it1,
    [it2, ..., itN])

    把 it 中的各个元素传给func,产出结果;如果传入
    N 个可迭代的对象,那么 func 必须能接受 N 个参
    数,而且要并行处理各个可迭代的对象

    itertools starmap(func, it)

    把 it 中的各个元素传给 func ,产出结果;输入的
    可迭代对象应该产出可迭代的元素 iit ,然后以
    func(*iit) 这种形式调用 func

    #演示 itertools.accumulate 生成器函数
    >>> sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
    >>> import itertools
    >>> list(itertools.accumulate(sample)) #
    [5, 9, 11, 19, 26, 32, 35, 35, 44, 45]
    12
    12
    >>> list(itertools.accumulate(sample, min)) #
    [5, 4, 2, 2, 2, 2, 2, 0, 0, 0]
    >>> list(itertools.accumulate(sample, max)) #
    [5, 5, 5, 8, 8, 8, 8, 8, 9, 9]
    >>> import operator
    >>> list(itertools.accumulate(sample, operator.mul)) #
    [5, 20, 40, 320, 2240, 13440, 40320, 0, 0, 0]
    >>> list(itertools.accumulate(range(1, 11), operator.mul))
    [1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800] #

    ❶ 计算总和。
    ❷ 计算最小值。
    ❸ 计算最大值。
    ❹ 计算乘积。
    ❺ 从 1! 到 10!,计算各个数的阶乘。

    演示用于映射的生成器函数

    >>> list(enumerate('albatroz', 1)) #
    [(1, 'a'), (2, 'l'), (3, 'b'), (4, 'a'), (5, 't'), (6, 'r'), (7, 'o'), (8, 'z')]
    >>> import operator
    >>> list(map(operator.mul, range(11), range(11))) #
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
    >>> list(map(operator.mul, range(11), [2, 4, 8])) #
    [0, 4, 16]
    >>> list(map(lambda a, b: (a, b), range(11), [2, 4, 8])) #
    [(0, 2), (1, 4), (2, 8)]
    >>> import itertools
    >>> list(itertools.starmap(operator.mul, enumerate('albatroz', 1))) #
    ['a', 'll', 'bbb', 'aaaa', 'ttttt', 'rrrrrr', 'ooooooo', 'zzzzzzzz']
    >>> sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
    >>> list(itertools.starmap(lambda a, b: b/a,
    ... enumerate(itertools.accumulate(sample), 1))) #
    [5.0, 4.5, 3.6666666666666665, 4.75, 5.2, 5.333333333333333,
    5.0, 4.375, 4.888888888888889, 4.5]

    ❶ 从 1 开始,为单词中的字母编号。
    ❷ 从 0 到 10,计算各个整数的平方。
    ❸ 计算两个可迭代对象中对应位置上的两个元素之积,元素最少的那个可迭代对象到头后就停止。
    ❹ 作用等同于内置的 zip 函数。
    ❺ 从 1 开始,根据字母所在的位置,把字母重复相应的次数。
    ❻ 计算平均值。

    合并多个可迭代对象的生成器函数

    模块 函数 说明
    itertools chain(it1, ..., itN)

    先产出 it1 中的所有元素,然后产出 it2 中的
    所有元素,以此类推,无缝连接在一起

    itertools chain.from_iterable(it)

    产出 it 生成的各个可迭代对象中的元素,一个
    接一个,无缝连接在一起; it 应该产出可迭代
    的元素,例如可迭代的对象列表

    itertools

    product(it1, ..., itN,
    repeat=1)

    计算笛卡儿积:从输入的各个可迭代对象中获
    取元素,合并成由 N 个元素组成的元组,与嵌
    套的 for 循环效果一样; repeat 指明重复处理
    多少次输入的可迭代对象

    内置 zip(it1, ..., itN)

    并行从输入的各个可迭代对象中获取元素,产
    出由 N 个元素组成的元组,只要有一个可迭代
    的对象到头了,就默默地停止

    itertools

    zip_longest(it1, ...,
    itN, fillvalue=None)

    并行从输入的各个可迭代对象中获取元素,产
    出由 N 个元素组成的元组,等到最长的可迭代
    对象到头后才停止,空缺的值使用 fillvalue
    填充

    演示用于合并的生成器函数

    >>> list(itertools.chain('ABC', range(2))) #
    ['A', 'B', 'C', 0, 1]
    >>> list(itertools.chain(enumerate('ABC'))) #
    [(0, 'A'), (1, 'B'), (2, 'C')]
    >>> list(itertools.chain.from_iterable(enumerate('ABC'))) #
    [0, 'A', 1, 'B', 2, 'C']
    >>> list(zip('ABC', range(5))) #
    [('A', 0), ('B', 1), ('C', 2)]
    >>> list(zip('ABC', range(5), [10, 20, 30, 40])) #
    [('A', 0, 10), ('B', 1, 20), ('C', 2, 30)]
    >>> list(itertools.zip_longest('ABC', range(5))) #
    [('A', 0), ('B', 1), ('C', 2), (None, 3), (None, 4)]
    >>> list(itertools.zip_longest('ABC', range(5), fillvalue='?')) #
    [('A', 0), ('B', 1), ('C', 2), ('?', 3), ('?', 4)]

    ❶ 调用 chain 函数时通常传入两个或更多个可迭代对象。
    ❷ 如果只传入一个可迭代的对象,那么 chain 函数没什么用。
    ❸ 但是 chain.from_iterable 函数从可迭代的对象中获取每个元素,
    然后按顺序把元素连接起来,前提是各个元素本身也是可迭代的对象。
    ❹ zip 常用于把两个可迭代的对象合并成一系列由两个元素组成的元
    组。
    ❺ zip 可以并行处理任意数量个可迭代的对象,不过只要有一个可迭代
    的对象到头了,生成器就停止。
    ❻ itertools.zip_longest 函数的作用与 zip 类似,不过输入的所有
    可迭代对象都会处理到头,如果需要会填充 None。
    ❼ fillvalue 关键字参数用于指定填充的值。


    itertools.product 生成器是计算笛卡儿积的惰性方式;以惰性方式计算笛卡儿积。

    示例itertools.product 函数的用法。

    >>> list(itertools.product('ABC', range(2))) #
    [('A', 0), ('A', 1), ('B', 0), ('B', 1), ('C', 0), ('C', 1)]
    >>> suits = 'spades hearts diamonds clubs'.split()
    >>> list(itertools.product('AK', suits)) #
    [('A', 'spades'), ('A', 'hearts'), ('A', 'diamonds'), ('A', 'clubs'),
    ('K', 'spades'), ('K', 'hearts'), ('K', 'diamonds'), ('K', 'clubs')]
    >>> list(itertools.product('ABC')) #
    [('A',), ('B',), ('C',)]
    >>> list(itertools.product('ABC', repeat=2)) #
    [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'),
    ('B', 'C'), ('C', 'A'), ('C', 'B'), ('C', 'C')]
    >>> list(itertools.product(range(2), repeat=3))
    [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0),
    (1, 0, 1), (1, 1, 0), (1, 1, 1)]
    >>> rows = itertools.product('AB', range(2), repeat=2)
    >>> for row in rows: print(row)
    ...
    ('A', 0, 'A', 0)
    ('A', 0, 'A', 1)
    ('A', 0, 'B', 0)
    ('A', 0, 'B', 1)
    ('A', 1, 'A', 0)
    ('A', 1, 'A', 1)
    ('A', 1, 'B', 0)
    ('A', 1, 'B', 1)
    ('B', 0, 'A', 0)
    ('B', 0, 'A', 1)
    ('B', 0, 'B', 0)
    ('B', 0, 'B', 1)
    ('B', 1, 'A', 0)
    ('B', 1, 'A', 1)
    ('B', 1, 'B', 0)
    ('B', 1, 'B', 1)

    ❶ 三个字符的字符串与两个整数的值域得到的笛卡儿积是六个元组
    (因为 3 * 2 等于 6)。
    ❷ 两张牌('AK')与四种花色得到的笛卡儿积是八个元组。
    ❸ 如果传入一个可迭代的对象,product 函数产出的是一系列只有一
    个元素的元组,不是特别有用。
    ❹ repeat=N 关键字参数告诉 product 函数重复 N 次处理输入的各个
    可迭代对象。


    把输入的各个元素扩展成多个输出元素的生成器函数

    模块 函数 说明
    itertools combinations(it, out_len)

    把 it 产出的 out_len 个元素组合在
    一起,然后产出

    itertools

    combinations_with_replacement(it,
    out_len)

    把 it 产出的 out_len 个元素组合在
    一起,然后产出,包含相同元素的
    组合

    itertools count(start=0, step=1)

    从 start 开始不断产出数字,按
    step 指定的步幅增加

    itertools cycle(it)

    从 it 中产出各个元素,存储各个元
    素的副本,然后按顺序重复不断地
    产出各个元素

    itertools permutations(it, out_len=None)

    把 out_len 个 it 产出的元素排列在
    一起,然后产出这些排列; out_len
    的默认值等于 len(list(it))

    itertools repeat(item, [times])

    重复不断地产出指定的元素,除非
    提供 times ,指定次数

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

    count、repeat 和 cycle的用法。

    >>> ct = itertools.count() #
    >>> next(ct) #
    0
    >>> next(ct), next(ct), next(ct) #
    (1, 2, 3)
    >>> list(itertools.islice(itertools.count(1, .3), 3)) #
    [1, 1.3, 1.6]
    >>> cy = itertools.cycle('ABC') #
    >>> next(cy)
    'A'
    >>> list(itertools.islice(cy, 7)) #
    ['B', 'C', 'A', 'B', 'C', 'A', 'B']
    >>> rp = itertools.repeat(7) #
    >>> next(rp), next(rp)
    (7, 7)
    >>> list(itertools.repeat(8, 4)) #
    [8, 8, 8, 8]
    >>> list(map(operator.mul, range(11), itertools.repeat(5))) #
    [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50]

    ❶ 使用 count 函数构建 ct 生成器。
    ❷ 获取 ct 中的第一个元素。
    ❸ 不能使用 ct 构建列表,因为 ct 是无穷的,所以我获取接下来的 3
    个元素。
    ❹ 如果使用 islice 或 takewhile 函数做了限制,可以从 count 生成
    器中构建列表。
    ❺ 使用 'ABC' 构建一个 cycle 生成器,然后获取第一个元素
    ——'A'。
    ❻ 只有受到 islice 函数的限制,才能构建列表;这里获取接下来的 7
    个元素。
    ❼ 构建一个 repeat 生成器,始终产出数字 7。
    ❽ 传入 times 参数可以限制 repeat 生成器生成的元素数量:这里会
    生成 4 次数字 8。
    ❾ repeat 函数的常见用途:为 map 函数提供固定参数,这里提供的是
    乘数 5。


    组合学生成器函数会从输入的各个元素中产出多个值

    >>> list(itertools.combinations('ABC', 2)) #
    [('A', 'B'), ('A', 'C'), ('B', 'C')]
    >>> list(itertools.combinations_with_replacement('ABC', 2)) #
    [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]
    >>> list(itertools.permutations('ABC', 2)) #
    [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
    >>> list(itertools.product('ABC', repeat=2)) #
    [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'), ('B', 'C'),
    ('C', 'A'), ('C', 'B'), ('C', 'C')]

    ❶ 'ABC' 中每两个元素(len()==2)的各种组合;在生成的元组中,
    元素的顺序无关紧要(可以视作集合)。
    ❷ 'ABC' 中每两个元素(len()==2)的各种组合,包括相同元素的组
    合。
    ❸ 'ABC' 中每两个元素(len()==2)的各种排列;在生成的元组中,
    元素的顺序有重要意义。
    ❹ 'ABC' 和 'ABC'(repeat=2 的效果)的笛卡儿积。

    用于重新排列元素的生成器函数

    模块 函数 说明
    itertools groupby(it,key=None)

    产出由两个元素组成的元素,形式为 (key,
    group) ,其中 key 是分组标准, group 是生成器,
    用于产出分组里的元素

    内置 reversed(seq)

    从后向前,倒序产出 seq 中的元素; seq 必须是序
    列,或者是实现了 __reversed__ 特殊方法的对象

    itertools tee(it, n=2)

    产出一个由 n 个生成器组成的元组,每个生成器
    用于单独产出输入的可迭代对象中的元素

    itertools.groupby 函数的用法

    >>> list(itertools.groupby('LLLLAAGGG')) #
    [('L', <itertools._grouper object at 0x102227cc0>),
    ('A', <itertools._grouper object at 0x102227b38>),
    ('G', <itertools._grouper object at 0x102227b70>)]
    >>> for char, group in itertools.groupby('LLLLAAAGG'): #
    ... print(char, '->', list(group))
    ...
    L -> ['L', 'L', 'L', 'L']
    A -> ['A', 'A',]
    G -> ['G', 'G', 'G']
    >>> animals = ['duck', 'eagle', 'rat', 'giraffe', 'bear',
    ... 'bat', 'dolphin', 'shark', 'lion']
    >>> animals.sort(key=len) #
    >>> animals
    ['rat', 'bat', 'duck', 'bear', 'lion', 'eagle', 'shark',
    'giraffe', 'dolphin']
    >>> for length, group in itertools.groupby(animals, len): #
    ... print(length, '->', list(group))
    ...
    3 -> ['rat', 'bat']
    4 -> ['duck', 'bear', 'lion']
    5 -> ['eagle', 'shark']
    7 -> ['giraffe', 'dolphin']
    >>> for length, group in itertools.groupby(reversed(animals), len): #
    ... print(length, '->', list(group))
    ...
    7 -> ['dolphin', 'giraffe']
    5 -> ['shark', 'eagle']
    4 -> ['lion', 'bear', 'duck']
    3 -> ['bat', 'rat']
    >>>

    ❶ groupby 函数产出 (key, group_generator) 这种形式的元组。
    ❷ 处理 groupby 函数返回的生成器要嵌套迭代:这里在外层使用 for
    循环,内层使用列表推导。
    ❸ 为了使用 groupby 函数,要排序输入;这里按照单词的长度排序。
    ❹ 再次遍历 key 和 group 值对,把 key 显示出来,并把 group 扩展成
    列表。
    ❺ 这里使用 reverse 生成器从右向左迭代 animals。

    最后一个生成器函数是 iterator.tee,这个函数只有一个
    作用:从输入的一个可迭代对象中产出多个生成器,每个生成器都可以产出输入的各个元素。产出的生成器可以单独使用

    >>> list(itertools.tee('ABC'))
    [<itertools._tee object at 0x10222abc8>, <itertools._tee object at 0x10222ac08>]
    >>> g1, g2 = itertools.tee('ABC')
    >>> next(g1)
    'A'
    >>> next(g2)
    'A'
    >>> next(g2)
    'B'
    >>> list(g1)
    ['B', 'C']
    >>> list(g2)
    ['C']
    >>> list(zip(*itertools.tee('ABC')))
    [('A', 'A'), ('B', 'B'), ('C', 'C')]

    Python 3.3中新出现的句法:yield from

    如果生成器函数需要产出另一个生成器生成的值,传统的解决方法是使用嵌套的 for 循环。

    >>> def chain(*iterables):
    ... for it in iterables:
    ... for i in it:
    ... yield i
    ...
    >>> s = 'ABC'
    >>> t = tuple(range(3))
    >>> list(chain(s, t))
    ['A', 'B', 'C', 0, 1, 2]
    >>> def chain(*iterables):
    ... for i in iterables:
    ... yield from i
    ...
    >>> list(chain(s, t))
    ['A', 'B', 'C', 0, 1, 2]

    可以看出,yield from i 完全代替了内层的 for 循环。在这个示例中使用 yield from 是对的,而且代码读起来更顺畅,不过感觉更像是语法糖。
    除了代替循环之外,yield from 还会创建通道,把内层生成器直接与外层生成器的客户端联系起来。把生成器当成协程使用时,这个通道特别重要,不仅能为客户端代码生成值,还能使用客户端代码提供的值。




  • 相关阅读:
    jQuery对象初始化的多种传参数形式
    Ajax核心技术之XMLHttpRequest
    【转载】神奇的css属性pointer-events
    JavaScript学习笔记——错误处理
    jquery validate的漂亮css样式验证
    JavaScript Math和Number对象研究
    《JavaScript语言精粹》笔记
    HTML5版的String Avoider小游戏
    IE11 F12工具报错
    css之图片羽化处理
  • 原文地址:https://www.cnblogs.com/chenxuming/p/9690444.html
Copyright © 2011-2022 走看看