interator tips:
从Python3.4开始,检查对象x是否迭代,最准确的方法是调用iter(x)函数,如果不可迭代,再处理TypeErro异常。这比isinstance(x, abc.iterable)更准确,因为iter(x)函数会考虑到__getitem__
方法,而abc.Iterable类不考虑。
检查对象x是否是迭代器最好的方式是调用isinstance(x, abc.Iterator)
Iterables vs Iterators
如果对象实现了能返回迭代器的__iter__
方法,那么对象就是可迭代的。序列都可以迭代,因为实现了__getitem__
方法, 而且其参数是从零开始的索引,这种对象也可以迭代。
迭代器是这样的对象,实现了无参数的__next__
方法,返回序列中的下一个元素,如果没有元素了,就抛出StopIteration异常,Python中的迭代器还实现了__iter__
方法(必须return self),因此迭代器也可以迭代。
可迭代对象和迭代器之间的关系:Python从可迭代的对象中获取迭代器。
generator 生成器
只要Python函数中的定义体中有yield关键字,该函数就是生成器函数。调用生成器函数,会返回一个生成器对象。也就是说,生成器函数是生成器工厂。
所有的生成器都是迭代器,因为生成器完全实现了迭代器接口。
ArithmeticProgression Generator
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
import itertools
gen = itertools.takewhile(lambda n: n < 3, itertools.count(1, .5))
list(gen)
[1, 1.5, 2.0, 2.5]
Generator Functions n The Standard Library (filter)
def vowel(c):
return c.lower() in 'aeiou'
list(itertools.filterfalse(vowel, 'Aardvark')) # filter内置函数的反用
['r', 'd', 'v', 'r', 'k']
list(itertools.dropwhile(vowel, 'Aardvark')) # 在条件为false之后的第一次, 返回迭代器中剩下来的项
['r', 'd', 'v', 'a', 'r', 'k']
list(itertools.takewhile(vowel, 'Aardvark')) # 在vowel返回False时停止
['A', 'a']
list(itertools.compress('Aardvark', (1, 0, 1, 1, 0, 1))) # 并行比较两个可迭代对象,如果后者元素为True, 产出前者的值
['A', 'r', 'd', 'a']
list(itertools.islice('Aardvark', 1, 7, 2)) # 产出前者的切片,主要是惰性操作
['a', 'd', 'a']
Generator Functions (map)
sample = [5,4,2,8,7,6,3,0,9,1]
list(itertools.accumulate(sample))
[5, 9, 11, 19, 26, 32, 35, 35, 44, 45]
list(itertools.accumulate(sample, min))
[5, 4, 2, 2, 2, 2, 2, 0, 0, 0]
import operator
list(itertools.accumulate(sample, operator.mul))
[5, 20, 40, 320, 2240, 13440, 40320, 0, 0, 0]
list(map(operator.mul, range(11), [2, 4, 8]))
[0, 4, 16]
list(map(lambda a, b: (a, b), range(11), [2, 4, 8])) # zip built-in function does
[(0, 2), (1, 4), (2, 8)]
list(itertools.starmap(operator.mul, enumerate('albatroz', 1))) # [operator.mul(*('a', 1))]
['a', 'll', 'bbb', 'aaaa', 'ttttt', 'rrrrrr', 'ooooooo', 'zzzzzzzz']
list(itertools.starmap(lambda a, b: b/a, enumerate(itertools.accumulate(sample), 1))) # average
[5.0,
4.5,
3.6666666666666665,
4.75,
5.2,
5.333333333333333,
5.0,
4.375,
4.888888888888889,
4.5]
Generator Functions (merging)
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), [10, 20, 30, 40]))
[('A', 0, 10), ('B', 1, 20), ('C', 2, 30)]
list(itertools.zip_longest('ABC', range(5), fillvalue='?'))
[('A', 0), ('B', 1), ('C', 2), ('?', 3), ('?', 4)]
Generator FUnctions (Cartesian)笛卡尔积
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('AB', range(2), repeat=2))
[('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)]
一个元素产出多个值
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))) # 为map函数提供固定参数5
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
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.groupby('LLLLAAGGG'))
[('L', ),
('A', ),
('G', )]
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']
list(itertools.tee('ABC'))
[, ]
g1, g2 = itertools.tee('ABC')
next(g1)
'A'
next(g2)
'A'
next(g2)
'B'
list(g1)
['B', 'C']
list(zip(*itertools.tee('ABC')))
[('A', 'A'), ('B', 'B'), ('C', 'C')]
New Syntax in Python3.3: yield from
def chain(*iterables):
for i in iterables:
yield from i
list(chain('ABBC', range(3)))
['A', 'B', 'B', 'C', 0, 1, 2]
yiled from 除了可以代替循环, 还会创建通道,把内层生成器直接与外层生成器的客户端联系起来。把生成器当成协程使用时,这个通道特别重要,不仅能为客户端代码生成值,还能使用客户端代码提供的值。
Iterable Reducing Functions (可迭代规约函数)
all([1, 0, 3])
False
all([])
True
any([1, 0, 3])
True
any([])
False
g = (n for n in [0, 0.0, 7, 8])
any(g)
True
next(g)
8
iter 一个鲜为人知的用法
传入两个参数,使用常规的函数或任何可调用的对象创建迭代器。第一个参数必须是可调用对象,用于不断调用产出各个值;第二个值是哨符,这个是标记值,当可调用的对象返回这个值时,出发迭代器抛出StropIteration异常
from random import randint
def d6():
return randint(1, 6)
d6_iter = iter(d6, 1) # 但是不会输出1
for roll in d6_iter:
print(roll)
3
4
2
有个实用的例子,逐行读取文件,直到遇到空行或达文件末尾为止:
with open('mydata.txt') as fp:
for line in iter(fp.readline, ''):
process_line(line)
Iterator vs Generator
- 从接口上来看,迭代器协议定义了两个方法:
__next__
和__iter__
。生成器对象实现了这两个方法,因此从这方面来看,所有的生成器都是迭代器。 - 从实现方式来看,生成器可以用yield或是生成器表达式。
- 从概念上来说,迭代器用于遍历集合,从中产出元素。生成器可能无需遍历集合就能生成值,如range函数
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a+b