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模块生成等差数列
>>> 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 标准库中的生成器函数
本节专注与通用的函数:参数为任意的可迭代对象,返回值是生成器,用于生成选中的、计算出的和重新排列的元素。
第一组是用于过滤的生成器函数:从输入的可迭代对象中产出元素的子集,而且不修改元素本身。
示例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']
>>>
下一组是用于映射的生成器函数:在输入的单个可迭代对象中的各个元素上做计算,然后返回结果
示例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]
>>>
接下来的这一组是用于合并的生成器函数,这些函数都是从输入的多个可迭代对象中产生出元素。
示例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)
>>>
有些生成器函数会从一个元素中产出多个值,扩展输入的可迭代对象
示例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')]
>>>
最后一组生成器函数用于产出输入的可迭代对象中的全部元素,不过会以某种方式重新排列。
示例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 可迭代的归约函数
接受一个可迭代的对象,然后返回单个结果。这些函数叫作“归约”函数。
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
-
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
-
Return
True
if any element of the iterable is true. If the iterable is empty, returnFalse
. 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