记录博客 ZH-BLOG

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

时间:2018-08-20 17:31:08分类:python

iter函数

解释器需要迭代对象 x 时, 会自动调用 iter(x)。

内置的 iter 函数有以下作用:

(1) 检查对象是否实现了 __iter__ 方法, 如果实现了就调用它, 获取一个迭代器。

(2) 如果没有实现 __iter__ 方法, 但是实现了 __getitem__ 方法,Python 会创建一个迭代器, 尝试按顺序(从索引 0 开始) 获取元素。

(3) 如果尝试失败, Python 抛出 TypeError 异常, 通常会提示“C object is not iterable”(C 对象不可迭代) , 其中 C 是目标对象所属的类。

可迭代的对象

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

迭代器

标准的迭代器接口有两个方法

__next__

返回下一个可用的元素, 如果没有元素了, 抛出 StopIteration 异常。

__iter__

返回 self, 以便在应该使用可迭代对象的地方使用迭代器。

实例可以是可迭代的对象, 但自身不要是迭代器。 

生成器

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

>>> t = T()
>>> import collections
>>> isinstance(t, collections.Iterable)
True
>>> isinstance(t, collections.Iterator)
False
>>> list(t)
[1, 2, 3]
>>> gt = iter(t)
>>> gt
generator object T.__iter__ at 0x00000000031537D8

生成器函数

>>> def gen_AB():
	print('start...')
	yield 'A'
	print('continue...')
	yield 'B'
	print('end')

	
>>> res1 = [x*3 for x in gen_AB()]
start...
continue...
end
>>> res1
['AAA', 'BBB']
>>> res2 = (x*3 for x in gen_AB())
>>> res2
generator object  at 0x0000000003153830
>>> for i in res2:
	print('-->', i)

	
start...
--> AAA
continue...
--> BBB
end

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

>>> from fractions import Fraction
>>> g = aritprog_gen(1, Fraction(1, 3), 3) # 会应用强制转换规则
>>> g
itertools.takewhile object at 0x0000000003162DC8
>>> list(g)
[Fraction(1, 1), Fraction(4, 3), Fraction(5, 3), Fraction(2, 1), Fraction(7, 3), Fraction(8, 3)]
>>> g = aritprog_gen(1, .5, 3)
>>> list(g)
[1.0, 1.5, 2.0, 2.5]	

标准库中的生成器函数

(1) 用于过滤生成器函数

>>> def vowel(c):
	return c.lower() in 'aeiou'

>>> list(filter(vowel, 'Aardvark')) # 返回 vowel 为 True 的元素
['A', 'a', 'a']
>>> import itertools
>>> list(itertools.filterfalse(vowel, 'Aardvark'))	# 返回 vowel 为 False 的元素
['r', 'd', 'v', 'r', 'k']
>>> list(itertools.dropwhile(vowel, 'Aardvark')) # 跳过 vowel 为 True 的元素,然后将剩下的元素依次返回
['r', 'd', 'v', 'a', 'r', 'k']
>>> list(itertools.takewhile(vowel, 'Aardvark')) # 返回 vowel 为 True 的元素,False 后立即停止
['A', 'a']
>>> list(itertools.compress('Aardvark', (1,0,1,1,0,1))) # 并行两个可迭代对象
['A', 'r', 'd', 'a']
>>> list(itertools.islice('Aardvark', 4)) # 返回 0 - 4 切片生成器
['A', 'a', 'r', 'd']
>>> list(itertools.islice('Aardvark', 4, 7)) # 返回 4 - 7 切片生成器
['v', 'a', 'r']
>>> list(itertools.islice('Aardvark', 1, 7, 2)) # 返回 1 - 7 - 2 切片生成器
['a', 'd', 'a']

(2) 用于映射的生成器函数

>>> 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] 
>>> list(itertools.accumulate(sample, min)) # 依次与结果应用 min
[5, 4, 2, 2, 2, 2, 2, 0, 0, 0]
>>> list(itertools.accumulate(sample, max)) # 依次与结果应用 max 
[5, 5, 5, 8, 8, 8, 8, 8, 9, 9]
>>> import operator
>>> list(itertools.accumulate(sample, operator.mul)) # 依次与结果应用 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]
>>> list(enumerate('albatroz', 1)) # 从 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))) # 将可迭代对象应用与 mul
[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)]
>>> list(itertools.starmap(operator.mul, enumerate('albatroz', 1)))	# 将可迭代对象的元素传给 func, 相当于 operator.mul(*ele)
['a', 'll', 'bbb', 'aaaa', 'ttttt', 'rrrrrr', 'ooooooo', 'zzzzzzzz']
>>> 'a' * 0
''
>>> 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]

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

>>> 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), fillvalue='?'))
[('A', 0), ('B', 1), ('C', 2), ('?', 3), ('?', 4)]
>>> 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('ABC', repeat=3))
[('A', 'A', 'A'), ('A', 'A', 'B'), ('A', 'A', 'C'), ('A', 'B', 'A'), ('A', 'B', 'B'), ('A', 'B', 'C'), ('A', 'C', 'A'), ('A', 'C', 'B'), ('A', 'C', 'C'), ('B', 'A', 'A'), ('B', 'A', 'B'), ('B', 'A', 'C'), ('B', 'B', 'A'), ('B', 'B', 'B'), ('B', 'B', 'C'), ('B', 'C', 'A'), ('B', 'C', 'B'), ('B', 'C', 'C'), ('C', 'A', 'A'), ('C', 'A', 'B'), ('C', 'A', 'C'), ('C', 'B', 'A'), ('C', 'B', 'B'), ('C', 'B', 'C'), ('C', 'C', 'A'), ('C', 'C', 'B'), ('C', 'C', 'C')]
>>> rows = itertools.product('AB', range(2), repeat=2) # repeat 重复可迭代对象的次数
>>> 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)

(4) 输出元素的生成器函数

>>> 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') # 不断重复产出 'ABC'
>>> next(cy)
'A'
>>> list(itertools.islice(cy, 7))
['B', 'C', 'A', 'B', 'C', 'A', 'B']
>>> rp = itertools.repeat(7) # 重复产出 7
>>> next(rp), next(rp)
(7, 7)
>>> list(itertools.repeat(8, 4)) # 重复 4 次产出 8
[8, 8, 8, 8]
>>> list(map(operator.mul, range(11), itertools.repeat(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.product('ABC', repeat=2))
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'), ('B', 'C'), ('C', 'A'), ('C', 'B'), ('C', 'C')]

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

>>> list(itertools.groupby('LLLLAAGGG'))
[('L', ), ('A', ), ('G', )]
>>> for char, group in itertools.groupby('LLLLAAAGG'): # 同类排序
	print(char, '->', list(group))

	
L -> ['L', 'L', 'L', 'L']
A -> ['A', 'A', 'A']
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')) # 返回由 2 个生成器组成的元组
[itertools._tee object at 0x000000000318C908, itertools._tee object at 0x0000000003194148]
>>> g1, g2 = itertools.tee('ABC')
>>> next(g1)
'A'
>>> next(g2)
'A'
>>> list(g1)
['B', 'C']
>>> next(g2)
'B'
>>> list(g2)
['C']
>>> list(zip(*itertools.tee('ABC')))
[('A', 'A'), ('B', 'B'), ('C', 'C')]

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]
>>> 
>>> def chain(*iterables):
	for it in iterables:
		yield from it

		
>>> list(chain(s, t))
['A', 'B', 'C', 0, 1, 2]

iter(object[, sentinel])

如果第二个参数部位 None,则第一个参数必须是可调用的,每次 __next__ 都会调用它, 直到等于第二个参数,抛出 StopIteration。

>>> from random import randint
>>> def d6():
	return randint(1, 6)

>>> it = iter(d6, 1)
>>> it
callable_iterator object at 0x0000000002E32438
>>> for roll in it:
	print(roll)

	
4
3

逐行读取文件, 直到遇到空行或者到达文件末尾为止。

with open('mydata.txt') as fp:
	for line in iter(fp.readline, '\n'):
		process_line(line)