《流畅的Python》读书笔记17(第十四章:可迭代的对象、迭代器和生成器2)

67 阅读6分钟

14.8 等差数列生成器

示例14-10 实现一个等差数列

class ArithmeticProgression:

    def __init__(self, begin, step, end=None):
        self.begin = begin
        self.step = step
        self.end = end

    def __iter__(self):
        result = type(self.begin + self.step)(self.begin) # 强制类型转换
        forever = self.end is None
        index = 0
        while forever or result < self.end:
            yield result
            index += 1
            result = self.begin + self.step * index

示例14-11 演示ArthmeticProgression类的用法

ap = ArithmeticProgression(0, 1, 3)
list(ap)
[0, 1, 2]
ap = ArithmeticProgression(0, .5, 3)
list(ap)
[0.0, 0.5, 1.0, 1.5, 2.0, 2.5]
ap = ArithmeticProgression(0, 1/3, 1)
list(ap)
[0.0, 0.3333333333333333, 0.6666666666666666]
from fractions import Fraction
ap = ArithmeticProgression(0, Fraction(1,3), 1)
list(ap)
[Fraction(0, 1), Fraction(1, 3), Fraction(2, 3)]
from decimal import Decimal
ap = ArithmeticProgression(0, Decimal('.1'), .3)
list(ap)
[Decimal('0'), Decimal('0.1'), Decimal('0.2')]

如果一个类只是为了构建生成器而去实现__iter__方法,那还不如使用生成器函数。

>>> def aritprog_gen(begin, step, end=None):
...     result = type(begin + step)(begin)
...     forever = end is None
...     index = 0
...     while forever or result < end:
...         yield result
...         index += 1
...         result = begin + step * index

使用itertools模块生成等差数列

docs.python.org/3/library/i…

>>> import itertools
>>> gen = itertools.count(1, .5)
>>> next(gen)
1
>>> next(gen)
1.5
>>> next(gen)
2.0
>>> next(gen)
2.5

itertools.count函数从不停止。

不过,itertools.takewhile函数则不同,它会生成一个使用另一个生成器的生成器,在指定的条件计算结果为False时停止。

>>> gen = itertools.takewhile(lambda n: n<3, itertools.count(1, .5) )
>>> list(gen)
[1, 1.5, 2.0, 2.5]

示例14-13 与前面的aritprog_gen函数作用相同

>>> def aritprog_gen(begin, step, end=None):
...     first = type(begin + step)(begin)
...     ap_gen = itertools.count(first, step)
...     if end is not None:
...         ap_gen = itertools.takewhile(lambda n:n<end, ap_gen)
...     return ap_gen

14.9 标准库中的生成器函数

本节专注与通用的函数:参数为任意的可迭代对象,返回值是生成器,用于生成选中的、计算出的和重新排列的元素。

第一组是用于过滤的生成器函数:从输入的可迭代对象中产出元素的子集,而且不修改元素本身。

middle_img_v2_be681915-bb21-4d61-9d4f-d77a2ea17d8g.jpg

示例14-14 演示用于过滤的生成器函数

>>> 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.islice('Aardvark',4)) #islice(it,stop)

['A', 'a', 'r', 'd']

>>> list(itertools.islice('Aardvark',4, 7))# islice(it,start,stop,step=1)

['v', 'a', 'r']

>>> list(itertools.islice('Aardvark',1, 7, 2))#islice(it,stop,step)

['a', 'd', 'a']

>>>

下一组是用于映射的生成器函数:在输入的单个可迭代对象中的各个元素上做计算,然后返回结果 middle_img_v2_54b65e48-1dc8-4db4-8fd1-07e46353c25g.jpg

示例14-15 演示itertools.accumulate生成器函数

>>> sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]

>>> import itertools

>>> 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]

>>>

示例14-16 演示用于映射的生成器函数

>>> list(enumerate('albatroz', 1))

[(1, 'a'), (2, 'l'), (3, 'b'), (4, 'a'), (5, 't'), (6, 'r'), (7, 'o'), (8, 'z')]

>>> list(enumerate('albatroz', 4))

[(4, 'a'), (5, 'l'), (6, 'b'), (7, 'a'), (8, 't'), (9, 'r'), (10, 'o'), (11, '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]

>>>

接下来的这一组是用于合并的生成器函数,这些函数都是从输入的多个可迭代对象中产生出元素。 middle_img_v2_57b3e139-2a0c-4b96-aa74-4b4aa87b385g.jpg 示例14-17 演示用于合并的生成器函数

>>> import itertools

>>> 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)]

>>>

示例14-18 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)

>>>

有些生成器函数会从一个元素中产出多个值,扩展输入的可迭代对象 middle_img_v2_783434f5-7672-46d0-aa69-8ae0e8bf3bag.jpg 示例14-19 演示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]

>>> import operator

>>> list(map(operator.mul, range(11), itertools.repeat(5)))

[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50]

>>>

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

>>> 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')]

>>>

最后一组生成器函数用于产出输入的可迭代对象中的全部元素,不过会以某种方式重新排列。 middle_img_v2_c613d53d-d3b3-493e-b9fd-35cd71e2721g.jpg 示例14-21 itertools.groupby函数的用法:

>>> list(itertools.groupby('LLLLAAGGG'))

[('L', <itertools._grouper object at 0x7f8508278ef0>), ('A', <itertools._grouper object at 0x7f8508278cc0>), ('G', <itertools._grouper object at 0x7f8508278fd0>)]

>>> for char, group in itertools.groupby('LLLLAAGGG'):

...     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']

>>>

示例4-22 itertools.tee函数产出多个生成器,每个生成器都可以产出输入的各个元素

>>> list(itertools.tee('ABC'))

[<itertools._tee object at 0x7f8508282488>, <itertools._tee object at 0x7f8508282348>]

>>> 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')]

>>>

14.10 Python3.3中新出现的句法: yield from

>>> 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]

使用yield from改写

>>> def chain(*iterables):

...     for i in iterables:

...         yield from i

... 

>>> s = 'ABC'

>>> t = tuple(range(3))

>>> list(chain(s, t))

['A', 'B', 'C', 0, 1, 2]

14.11 可迭代的归约函数

middle_img_v2_0ff1ed4a-0007-4016-aaf3-aa76ac0c52ag.jpg 接受一个可迭代的对象,然后返回单个结果。这些函数叫作“归约”函数。

all和any函数会短路(一旦确定了结果就立即停止使用迭代器)。

示例14-23 把几个序列传给all和any函数后得到结果

>>> all([1,2,3])

True

>>> all([1,0,3])

False

>>> all([]) # 见下方说明

True

>>> any([1,2,3])

True

>>> any([1,0,3])

True

>>> any([0, 0.0])

False

>>> any([]) # 见下方说明

False

>>> g = (n for n in [0, 0.0, 7, 8])

>>> any(g)

True

>>> next(g)

8
  • all(iterable)

  • Return True if all elements of the iterable are true (or if the iterable is empty). Equivalent to:

    def all(iterable):
        for element in iterable:
            if not element:
                return False
        return True
    
  • any(iterable)

    Return True if any element of the iterable is true. If the iterable is empty, return False. Equivalent to:

    def any(iterable):
        for element in iterable:
            if element:
                return True
        return False
    

14.12 深入分析iter函数

在Python中迭代对象x时会调用iter(x).

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

下述示例展示如何使用iter函数掷骰子,直到掷出1点为止:

>>> from random import randint

>>> def d6():

...     return randint(1,6)

... 

>>> d6_iter = iter(d6, 1)

>>> d6_iter

<callable_iterator object at 0x7f85082814e0>

>>> for roll in d6_iter:

...     print(roll)

... 

2

3

4

5

3

3

6