可迭代的对象,迭代器和生成器
可迭代对象
迭代是数据处理的基石。
扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式。
迭代器模式:按需一次获取一个数据项
序列可以迭代的原因:iter 函数
解释器需要迭代对象X时,会自动调用 iter(x)
内置的iter函数作用有:
(1)检查对象时候有__iter__方法,如果实现了就调用它,获取迭代器。
(2)如果没有实现__iter__方法,但是实现了__getitem__方法,Python会创建一个迭代器,尝试按顺序(从索引0开始)获取元素
(3)如果尝试失败,Python会抛出TypeError异常。
任何序列都可以迭代的原因是,它们都实现了__getitem__方法,通常情况下,标准序列都会实现__iter__方法。(鸭子类型体现)
检查对象是否可以迭代:
(1)iter(X) # 考虑__getitem__方法
(2)issubclass(X,abc.Iterable) # 不考虑__getitem__方法
可迭代的对象:
使用 iter() 内置函数可以获取迭代器的对象。(实现了__iter__方法,或者实现__getitem__方法,而且参数从0开始)
Python从可迭代的对象中获取迭代器。
For循环实质:
利用While循环模式
>>> s = 'ABC' >>> it = iter(s) >>> while True: try: print(next(it)) except StopIteration: del it break
(1)使用可迭代对象获取迭代器it
(2)不断在迭代器上调用next函数,获取下一个字符。
(3)如果没有字符了,迭代器会抛出StopIteration异常。
(4)释放对it的引用。
(5)退出循环
迭代器
迭代器接口:
(1)__next__ 返回下一个可用的元素,如果没有元素了,抛出StopIteration异常。
(2)__iter__ 返回self。迭代器也可以迭代
误区:
迭代器可以迭代,但是可迭代的对象不是迭代器。
可迭代对象必须实现__iter__方法,但不能实现__next__方法。
迭代器的__iter__方法应该返回自身。
生成器
生成器函数:
使用 yield 关键字构建。
生成器函数不会抛出StopIteration异常,而是在生成完全部数值后直接退出。
只要Python函数定义体中有yield关键字,该函数就是生成器函数。
调用生成器函数时,会返回一个生成器对象。
def gen_123(): yield 1 yield 2 yield 3 >>> gen_123 <function gen_123 at 0x00000166BD887F28> >>> gen_123() <generator object gen_123 at 0x00000166BD9BDF68>
生成器表达式:
列表推导的惰性版本:不会迫切的构建列表没事返回一个生成器,按需惰性生成元素。
>>> 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
列表推导迫切地迭代gen_AB()函数生成的生成器对象,产出元素。
>>> res2 = (x*3 for x in gen_AB()) >>> res2 <generator object <genexpr> at 0x000001D48970F2B0> >>> for i in res2: print('-->',i) Start --> AAA Continue --> BBB End
▲ 所有生成器都是迭代器,生成器完全实现了迭代器接口。
▲ 迭代器用于从集合中取出元素;生成器用于“凭空”生成元素。
▲ Python社区中大多数时候都把迭代器和生成器视作同一个概念
(1)等差数列生成器例子:
>>> ap = ArithmeticProgression(0,1,3) >>> list(ap) [0,1,2] >>> from fractions import Fraction >>> ap = ArithmeticProgression(0,Fraction(1,3),1) >>> list(ap) [Fraction(0,3),Fraction(1,3),Fraction(2,3)] >>> from decimal import Decimal >>> ap = ArithmeticProgression(0,Decimal('.1'),.3) >>> list(ap) [Decimal('0.0'),Decimal('0.1'),Decimal('0.2')]
class ArithmeticProgression: def __init__(self,begin,step,end=None): self.begin = begin self.step = step self.end = end def __iter__(self): forever = self.end is None index = 0 result = type(self.begin + self.step)(self.begin) while forever or index < self.end : yield result index += 1 result = self.begin + index * self.step ##### 函数版本: def aritprog_gen(begin,step,end): forever = end is None index = 0 result = type(begin +step)(begin) while forever or index < end: yield result index += 1 result = begin + index * step
(2)使用 itertools模块生成等差数列
itertools.count 函数返回的生成器能生成多个数。然而,itertools.count 函数从不停止,因此调用 list(count()),会超出可以内存。
itertools.takewhile 函数会生成一个使用另一个生成器的生成器,在指定的条件计算结果为False时停止。因此,两个函数可以结合使用。
>>> gen = itertools.takewhile(lambda x : x < 3 , itertools.count(1,.5)) >>> list(gen) [1,1.5,2.0,2.5]
标准库中的生成器函数
标准库中的生成器函数:
一、用于过滤的生成器函数 :从输入的可迭代对象中产出元素的子集,而且不修改元素本身。
(1)itertools.compress(it, selector_it),并行处理两个可迭代的对象,如果 selector_it中的元素是真值,产出it中对应的元素。
(2)itertools.dropwhile(predicate, it),处理it,跳过predicate计算结果为真值的元素,产出剩下的各个元素。
(3)filter(predicate, it),把it中的各个元素传给predicate,如果predicate(item)返回True,产出对应的元素。
(4)itertools.filterfalse(predicate, it),如果predicate(item)返回False,产出对应的元素。
(5)itertools.islice(it, stop) 或 itertools.islice(it, start, stop, step=1),产出it的切片。
(6)itertools.takewhile(predicate, it),predicate返回True时产出对应的元素,然后立即停止,不再继续检查。
>>> import itertools >>> >>> def vowel(c): ... return c.lower() in 'aeiou' ... >>> list(itertools.compress('Aardvark',(1,0,1,1,1,0))) ['A', 'r', 'd', 'v'] >>> list(itertools.dropwhile(vowel,'Aardvark')) ['r', 'd', 'v', 'a', 'r', 'k'] >>> list(filter(vowel,'Aardvark')) ['A', 'a', 'a'] >>> list(itertools.filterfalse(vowel,'Aardvark')) ['r', 'd', 'v', 'r', 'k'] >>> list(itertools.islice('Aardvark',4)) ['A', 'a', 'r', 'd'] >>> list(itertools.islice('Aardvark',1,7,2)) ['a', 'd', 'a'] >>> list(itertools.takewhile(vowel,'Aardvark')) ['A', 'a']
二、用于映射的生成器函数 :对输入的可迭代对象中的元素做计算,然后返回结果。
(1)itertools.accumulate(it, [func]),产出累积的总和,如果提供了func,那么把前两个元素传给它,然后把计算结果和下一个元素传给它。
(2)enumerate(iterable, start=0),产出由两个元素组成的元组,结构是(index, item),其中index从start开始计数,item则从iterable中获取。
(3)map(func, it1,[it2, ..., itn]),把it中的各个元素传给func,产出结果。如果传入N个可迭代的对象,func必须能接受N个参数,要并行处理各个可迭代对象。
(4)itertools.starmap(func, it),把it中的各个元素传给func,产出结果。输入的可迭代对象应该产出可迭代的元素iit,然后以func(*iit),形式调用func。
>>> import itertools >>> 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] >>> list(itertools.accumulate(sample,max)) [5, 5, 5, 8, 8, 8, 8, 8, 9, 9] >>> import operator >>> list(itertools.accumulate(range(1,11),operator.mul)) [1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800] >>> list(enumerate('Aardvark',1)) [(1, 'A'), (2, 'a'), (3, 'r'), (4, 'd'), (5, 'v'), (6, 'a'), (7, 'r'), (8, 'k')] >>> list(map(operator.mul,range(0,11),[2,4,8])) [0, 4, 16] >>> list(map(lambda a,b:(a,b),range(0,11),(2,4,8))) [(0, 2), (1, 4), (2, 8)] >>> list(itertools.starmap(operator.mul,enumerate('Aardvark',1))) ['A', 'aa', 'rrr', 'dddd', 'vvvvv', 'aaaaaa', 'rrrrrrr', 'kkkkkkkk'] >>> 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)itertools.chain(it1, ..., itN),先产出it1中的所有元素,然后产出it2,以此类推,连接在一起。
(2)itertools.chain.from_iterable(it),产出it生成的各个可迭代对象中的元素,一个一个连接在一起。
(3)itertools.product(it1, ..., itN, repeat=1),计算笛卡尔积,repeat指名重复处理多少次输入的可迭代对象
(4)zip(it1 , ..., itN),从输入的各个可迭代对象中获取元素,产出由N个元素组成的元组。只要有一个可迭代对象到头就停止。
(5)itertools.zip_longest(it1, ..., itN, fillvalue=None),从输入的各个可迭代对象中获取元素,产出由N个元素组成的元组。空缺值用fillvalue填充。
>>> list(itertools.chain.from_iterable('ABC')) ['A', 'B', 'C'] >>> list(itertools.chain.from_iterable(enumerate('ABCD',1))) [1, 'A', 2, 'B', 3, 'C', 4, 'D'] >>> list(itertools.product('ABC',range(2))) [('A', 0), ('A', 1), ('B', 0), ('B', 1), ('C', 0), ('C', 1)] >>> 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(zip(range(0,10),'ABCD')) [(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D')] >>> list(itertools.zip_longest(range(0,10),'ABCD',)) [(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D'), (4, None), (5, None), (6, None), (7, None), (8, None), (9, None)]
四、用于扩展输入的可迭代对象:从一个元素中产出多个值。
(1)itertools.combinations(it, out_len),把it产出的out_len个元素组合在一起,然后产出。
(2)itertools.combinations_with_replacement(it, out_len),把it产出的out_len个元素组合在一起,然后产出。包含相同元素的组合。
(3)itertools.count(start=0, step=1),从start开始不断产出数字,按step指定的步幅增加。
(4)itertools.cycle(it),从it中产出各个元素,存储各个元素的副本,然后按顺序重复不断地产出各个元素。
(5)itertools.permutations(it, out_len=None),把out_len个it产出的元素排列在一起,然后产出这些排列。(注重顺序)
(6)itertools.repeat(item, [times]),重复不断地产出指定的元素,除非提供times,指定次数。
>>> 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.repeat('ABC',2)) ['ABC', 'ABC'] >>> cy = itertools.cycle('ABC') >>> list(itertools.islice(cy,7)) ['A', 'B', 'C', 'A', 'B', 'C', 'A']
五、用于重新排列的生成器。
(1)itertools.groupby(it, key=None),产出由两个元素组成的元素,形式为(key,group)其中key是分组标准。(假定输入的可迭代对象使用分组标准排序)
(2)reversed(seq),从后向前,倒序产出seq中的元素,seq必须是序列,或者实现了__reversed__特殊方法的对象。
(3)itertools.tee(it, n=2),产出一个由n个生成器组成的元祖,每个生成器用于单独产出输入的可迭代对象中的元素
>>> list(itertools.groupby('LLLAAGGG')) [('L', <itertools._grouper object at 0x000001AB8949B860>),
('A', <itertools._grouper object at 0x000001AB8949B898>),
('G', <itertools._grouper object at 0x000001AB894990F0>)] >>> animals = ['duck','eagle','rat','giraffe','bear','bat','dolphin'] >>> animals.sort(key=len) >>> for length,group in itertools.groupby(animals,len): ... print(length,'->',list(group)) 3 -> ['rat', 'bat'] 4 -> ['duck', 'bear'] 5 -> ['eagle'] 7 -> ['giraffe', 'dolphin'] >>> list(itertools.tee('ABC')) [<itertools._tee object at 0x000001AB89494EC8>, <itertools._tee object at 0x000001AB89498488>] >>> list(zip(*itertools.tee('ABC'))) [('A', 'A'), ('B', 'B'), ('C', 'C')]
yield from
yield from句法:
生成器函数需要产出另一个生成器生成的值,传统解决方法是使用嵌套的for循环。
def chain(*iterables): for it in iterables: for i in it: yield i s = 'ABC' t = tuple(range(3)) print(list(chain(s,t))) # ['A', 'B', 'C', 0, 1, 2]
使用yield from 代替内层for:
def chain(*iterables): for it in iterables: yield from it
归约 /合拢 /累加函数
可迭代的归约函数 /合拢函数 /累加函数 :
(1)all(it),it中的所有元素都为真值时返回True,否则返回False;all([]) 返回 True、all(0) 返回 False
(2)any(it),it中的有元素为真值时返回True,否则返回False; any([]) 返回 False、any([0,0.0]) 返回 False
(3)max(it, [key=,], [default=]),返回it中最大值,key是排序函数,与sorted函数一样,如果可迭代对象为空,返回default。
(4)min(it, [key=,], [default=]),返回it中最小值,key是排序函数,与sorted函数一样,如果可迭代对象为空,返回default。
(5)functools.reduce(func, it, [initial]),把前两个元素传给func,然后把计算结果和第三个元素传给func,以此类推,返回最后的结果,initial当作第一个元素传入。
(6)sum(it, start=0),it中所有元素的总和,如果提供start,会先把它加上。
>>> g = (n for n in [0,0.0,7,8]) >>> any(g) True >>> next(g) 8
深入 iter 函数
深入分析 iter 函数 :
在Python中迭代对象x时会调用 iter(x)。
iter函数还可以传入第二个参数。哨符,这是个标记值,当可调用的对象返回这个值时,触发迭代器抛出StopIteration异常,而不产出哨符。
def d6(): return random.randint(1,6) d6_iter = iter(d6,1) for roll in d6_iter: print(roll)
用来处理读取文件,直到遇到空行或者到达文件末尾为止:
with open('mydata.bat') as fp: for line in iter(fp.readline, '\n'): process_line(line)