可迭代的对象,迭代器和生成器

 

可迭代对象

迭代是数据处理的基石。

  扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式。

迭代器模式按需一次获取一个数据项

序列可以迭代的原因: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
ArithmeticProgression

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

 

posted @ 2019-07-30 15:33  5_FireFly  阅读(305)  评论(0编辑  收藏  举报
web
counter